Skip to content

fix(a11y): announce read-only state of form components to screen readers - #2

Open
it-rec wants to merge 19 commits into
mainfrom
claude/carbon-issue-22407-qesd41
Open

fix(a11y): announce read-only state of form components to screen readers#2
it-rec wants to merge 19 commits into
mainfrom
claude/carbon-issue-22407-qesd41

Conversation

@it-rec

@it-rec it-rec commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Form components previously did not communicate their read-only state to
assistive technology, or did so using poorly-supported aria-readonly
or misapplied aria-disabled (which announced editable fields as
"disabled"). This violated WCAG 2.1 SC 4.1.2 (Name, Role, Value).

Each affected component now renders a visually-hidden "Read only"
description that is referenced from the focusable control via
aria-describedby, providing a reliable, cross-screen-reader
announcement. Misapplied aria-disabled on read-only Dropdown,
MultiSelect and Checkbox group controls is replaced so disabled is only
announced for genuinely disabled controls. aria-readonly is kept only
on roles that support it.

Covers React and Web Components packages:
Checkbox, CheckboxGroup, Dropdown, MultiSelect, FilterableMultiSelect,
NumberInput, RadioButton, RadioButtonGroup, Select, Slider, Toggle
(and fluid variants, which inherit the behavior).

Fixes carbon-design-system#22407

Form components previously did not communicate their read-only state to
assistive technology, or did so using poorly-supported `aria-readonly`
or misapplied `aria-disabled` (which announced editable fields as
"disabled"). This violated WCAG 2.1 SC 4.1.2 (Name, Role, Value).

Each affected component now renders a visually-hidden "Read only"
description that is referenced from the focusable control via
`aria-describedby`, providing a reliable, cross-screen-reader
announcement. Misapplied `aria-disabled` on read-only Dropdown,
MultiSelect and Checkbox group controls is replaced so disabled is only
announced for genuinely disabled controls. `aria-readonly` is kept only
on roles that support it.

Covers React and Web Components packages:
Checkbox, CheckboxGroup, Dropdown, MultiSelect, FilterableMultiSelect,
NumberInput, RadioButton, RadioButtonGroup, Select, Slider, Toggle
(and fluid variants, which inherit the behavior).

Fixes carbon-design-system#22407
@it-rec
it-rec force-pushed the claude/carbon-issue-22407-qesd41 branch from 9c4e182 to 9c74872 Compare June 10, 2026 06:13
it-rec and others added 18 commits June 19, 2026 06:05
Address review feedback: `aria-disabled=${disabled}` serialized the
boolean `false` to the string "false", rendering `aria-disabled="false"`
in the common (enabled) case. Use `ifDefined` so the attribute is
omitted unless the control is genuinely disabled, matching the
`aria-readonly`/`aria-describedby` pattern used elsewhere in this change.
Address Codecov patch-coverage gaps for this change:
- slider: add tests for the range (two-handle) and hidden-text-input
  (tooltip) thumb render paths so the readonly aria-readonly/
  aria-describedby attributes on all four thumbs are exercised.
- multi-select: assert the clear-selection button gets aria-disabled
  when the component is disabled, covering that branch.
Address review feedback (adamalston):
- CheckboxGroup: drop the fieldset-level "Read only" description and its
  aria-describedby entry. Each child Checkbox already announces read-only,
  so the group node caused a duplicate announcement.
- RadioButtonGroup: propagate the group's readOnly to each child
  RadioButton (unless the child sets it), so the announcement lands on the
  focusable radio inputs, and remove the now-redundant fieldset-level
  description. aria-readonly stays on the fieldset for group semantics.
Address review feedback (adamalston): the visually-hidden "Read only"
announcement was hardcoded. Make it translatable following Carbon's
i18n guidance.

React: each affected component now exposes translateWithId with a
carbon.<component>.read-only message id defaulting to "Read only"
(Checkbox, Toggle, RadioButton, Select gain the translateWithId API;
NumberInput and Slider extend their existing translation maps; Dropdown,
MultiSelect and FilterableMultiSelect widen their translateWithId key
union and fall back to the default).

Web Components: each affected component exposes a read-only-text
attribute / readOnlyText property defaulting to "Read only".

Adds unit tests asserting a custom translation overrides the default.
The NumberInput test helper throws on unknown message ids; add the new
carbon.number-input.read-only id so readOnly renders that pass a custom
translateWithId no longer throw.
Address review feedback (adamalston):
- Checkbox: the input's aria-describedby now includes the helper text id
  (when shown) alongside the read-only announcement, so a read-only
  Checkbox still exposes its helper text to screen readers. Matches the
  existing NumberInput behavior.
- CheckboxGroup: replace the cx({ [helperId as string]: ... }) pattern
  (which relied on cx dropping a falsy computed key and used an incorrect
  type assertion) with a plain conditional string.
- MultiSelect/FilterableMultiSelect: drop the same helperId as string
  assertion; pass the ids to cx as string arguments instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[a11y]: SR does not announce readonly status of form elements

4 participants