Skip to content

Chip: accessibilityState passed by caller is discarded; aria-checked is never forwarded to the real rendered node #5146

Open

Description

@benincasantonio

Description

Chip destructures selected/disabled/etc. and computes its own accessibilityState={{ selected, disabled }} internally, but any accessibilityState (or aria-checked) a caller passes in falls into ...rest and is spread onto the outer, non-interactive Surface — never reaching the interactive TouchableRipple/Pressable node.

This is a real accessibility gap on web: react-native-web does not derive aria-* attributes from a native accessibilityState object at all (confirmed via react-native-web's forwardedProps/index.js allowlist, which only forwards a literal aria-checked prop straight to the DOM). So even though Chip's own internally-computed accessibilityState happens to reach the real Pressable on native (where RN's Pressable merges accessibilityState.checked itself), there is no equivalent path on web, and Chip never sets aria-checked itself.

Net effect: a Chip rendered as part of a custom radio/checkbox group (e.g. a chip-based single/multi-select control) reports checked: false in the web accessibility tree for every option, including the selected one — a screen reader user on web cannot tell which option is selected.

Reproduction

  1. Render a Chip with selected={true} inside PaperProvider, on web (react-native-web).
  2. Inspect the rendered DOM / accessibility tree.
  3. No aria-checked attribute is present anywhere in the output.

Expected behavior

Chip should forward a literal aria-checked prop (distinct from accessibilityState) through to the real interactive node it renders (TouchableRipple → Pressable), so consumers building a custom selectable-chip group can make selection state screen-reader-visible on web.

Additional context

  • Verified the in-flight 6.0.0 refactor (commit 466d9c608d2cb3dead7831b64c925f4523419b0e, PR refactor: migrate accessibility props to aria props #5005, "refactor: migrate accessibility props to aria props") does not fix this: it adds role/aria-label/aria-selected/aria-disabled forwarding to TouchableRipple, but Chip's own vocabulary there is selected/aria-selected, not checked/aria-checked — an unrecognized aria-checked is still spread via ...rest onto the non-interactive Surface in that version too.
  • react-native-paper version: 5.15.3 (latest stable at time of filing).
  • We've applied a local patch-package patch forwarding aria-checked through Chip → TouchableRipple → Pressable as a workaround; happy to share the patch/diff if useful upstream.

Activity

  1. dave-otf commented on Oct 27, 2026

    @dave-otf

    I checked the current main path: Chip.tsx sends ...rest to the outer Surface, while the interactive TouchableRipple only receives aria-label, role, aria-selected, and aria-disabled (lines 301–304). TouchableRipple forwards its props to Pressable, so a narrow fix can pull aria-checked out of rest and pass it there. A focused web regression test could render role="checkbox" with aria-checked true and false, then assert the attribute is on the actual interactive DOM node and absent from the outer Surface; that checks both placement and value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions