Lena Fischer·
I run every new component through this a11y state audit before review. It caught seven issues in our dropdown last week, including two I would have shipped without blinking.
Audits a UI component's interactive states against WCAG 2.2 and outputs a findings table with concrete fixes.
Component State Accessibility Audit
You are an accessibility engineer auditing a single UI component. Be precise and cite WCAG 2.2 success criteria by number.
Component code (markup + styles + behavior):
{{component_code}}
Design tokens in use (if any):
{{tokens}}
Audit every interactive state of this component: default, hover, focus-visible, active, disabled, error/invalid, loading, and any open/expanded states. For each state, check:
1. Keyboard: is the state reachable and operable without a mouse? Tab order, arrow-key behavior where the pattern requires it (per WAI-ARIA APG), Escape to dismiss, focus trap correctness.
2. Focus visibility: does focus-visible meet 2.4.7 and the 2.4.11 focus-appearance recommendations (contrast of the indicator against adjacent colors, minimum area)?
3. Contrast: text contrast (1.4.3) AND non-text contrast (1.4.11) for borders, icons, and state indicators — check the actual token values, do not assume.
4. ARIA: correct roles, states (aria-expanded, aria-selected, aria-invalid, aria-busy), and accessible name in every state. Flag redundant or conflicting ARIA.
5. Motion and timing: any animation over 5 seconds, anything that needs prefers-reduced-motion.
Output a findings table: # | State | Issue | WCAG criterion | Severity (blocker / serious / minor) | Fix (exact code or token change). After the table, list what the component does RIGHT — reviewers need to know what not to break. Finish with the three findings I should fix first and why those three.
Ergebnisse
## A11y State Audit: `<SelectDropdown />`
### Findings
| # | State | Issue | WCAG | Severity | Fix |
|---|-------|-------|------|----------|-----|
| 1 | open | Arrow keys do not move the active option — only Tab does, which leaves the listbox | 4.1.2 / APG listbox pattern | Blocker | Implement ArrowUp/ArrowDown on the listbox, track `aria-activedescendant` |
| 2 | open | Escape does not close the dropdown; focus is left in a trap | 2.1.2 | Blocker | Add Escape handler: close + return focus to trigger |
| 3 | focus-visible | Focus ring uses `--color-border-subtle` (#D4D4D8) on white — 1.35:1 against adjacent | 2.4.7 / 2.4.11 | Serious | Switch to `--color-focus-ring` (#2563EB), 2px offset ring → 3.9:1 |
| 4 | disabled | Disabled text `--text-disabled` (#A1A1AA) on `--bg-subtle` (#FAFAFA) = 2.4:1, but conveys the only "unavailable" signal | 1.4.3 note / 1.4.11 | Serious | Keep the color, add `aria-disabled` + cursor + an explanation in the trigger's accessible name |
| 5 | error | Red border only signals invalid — no text, no `aria-invalid` | 3.3.1 / 1.4.1 | Serious | Add `aria-invalid="true"` + visible error text linked via `aria-describedby` |
| 6 | loading | Spinner has no announcement; screen reader users get silence after selecting | 4.1.3 | Minor | Add `aria-busy` on the combobox and a polite live region: "Loading options" |
| 7 | expanded | `aria-expanded` lives on the wrapper div, not the button that gets focus | 4.1.2 | Minor | Move `aria-expanded` + `aria-haspopup="listbox"` onto the trigger button |
### What the component does right
- Trigger is a real `<button>` — no div soup, correct name from the selected value.
- Options render text at 15.2:1 contrast using `--text-primary`; no color-only selected state (checkmark icon present).
- Listbox unmounts cleanly, no leaked `aria-hidden` on the page.
### Fix first
1. **#1 arrow keys** — the component is currently unusable with a keyboard in its primary interaction. Everything else is polish until this works.
2. **#2 Escape** — trap without exit is a hard WCAG failure and a rage-inducer.
3. **#3 focus ring token** — one token swap, fixes every consumer of the component at once. Cheapest serious fix in the table.
Modell: Claude Sonnet 4.6
14 Likes7 SavesScore: 10
1 Kommentar
Emily Chen·
The 'what it does right' section is smart — half our a11y regressions come from fixing one thing and breaking another.
