Accessibility Implementation
Ship markup a keyboard and a screen reader can use. Prefer native elements. ARIA is a last resort that you must keep in sync with state.
To audit an existing UI, use accessibility-review. Design-system API work is design-system-contribution.
Workflow
- Name the widget and the equivalent native control (button, dialog, tabs, combobox, table).
- Structure: heading/landmark/list/label first; role only if no native.
- Keyboard map: Tab/Shift+Tab, Enter/Space, Escape, arrows if a composite. No trap except a modal that restores focus on close.
- Name, role, value: visible label,
aria-expanded/aria-selectedonly when the role requires them, and they must match JS state. - Focus: visible ring, logical order, initial focus in dialogs, return focus to the opener.
- Motion and contrast:
prefers-reduced-motion, do not convey meaning by color alone. - Test notes: keyboard path and one screen-reader smoke.
Output format
## A11y implementation: <widget>
**Native first:** …
### Markup
<!-- minimal correct structure -->
### Keyboard
| Key | Behavior |
### State / ARIA
what is synced, what is not used
### Focus
open / move / close
### Do not
…
Rules
<div onclick>is not a button. Use<button type="button">.- Do not add
role="button"to a real<button>. aria-labelloses to a visible<label>when both exist and disagree — make them the same, or drop the aria.- Modals: focus in, inert/aria-modal the rest, Escape + close button, restore focus.
- Live regions only for new status the user did not just type.
- Icon-only controls need an accessible name.
- Do not invent WCAG clause numbers. Describe the user path.
Edge cases
- Review request: if they only want findings, point at
accessibility-reviewand still offer the fix markup if they pasted broken code. - Custom select / combobox: this is hard; implement a known pattern (listbox + input) rather than inventing one.
- Canvas / chart: provide a text summary or data table, not ARIA on every pixel.
- SSR / hydration: do not flash a non-keyboard version first.
---