name: ensuring-accessibility description: Audits and fixes web UI for WCAG 2.2 AA compliance — keyboard navigation, visible focus, ARIA usage, alt text, form labels, color contrast, target sizes, and reduced motion. Use when the user asks to make a page or component accessible, run an accessibility or a11y audit, fix WCAG violations, add ARIA or alt text, improve keyboard or screen-reader support, or check color contrast. Do not use for general semantic markup structure (structuring-semantic-html) or visual design choices.
Ensuring Accessibility
When to use / when NOT to use
- Use for: auditing or fixing UI against WCAG 2.2 AA — keyboard access, focus, ARIA, alt text, labels, contrast, target sizes, motion preferences.
- Do NOT use for: choosing heading/landmark structure for its own sake (structuring-semantic-html), visual design or theming decisions, or backend logic.
Core rules
-
Semantic HTML first, ARIA last. ARIA only when no native element can express the role (first rule of ARIA).
- ✅
<button onclick="…">Save</button> - ❌
<div role="button" tabindex="0" onclick="…">Save</div>
- ✅
-
Everything interactive works by keyboard alone, in a logical Tab order, no traps, no drag-only interactions (WCAG 2.5.7).
- ✅ sortable list also offers "Move up/Move down" buttons
- ❌ reorder only via drag-and-drop
-
Focus must be visible with ≥3:1 contrast against adjacent colors; never remove it without a replacement.
- ✅
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; } - ❌
:focus { outline: none; }
- ✅
-
Contrast minimums: 4.5:1 for normal text, 3:1 for large text (≥24px or ≥18.7px bold) and for UI components/graphics.
-
Targets ≥24×24 CSS px (WCAG 2.2), 44×44px for primary touch targets.
-
Every image has an
alt: descriptive for informative images,alt=""for decorative ones — never omit the attribute.- ✅
<img src="chart.png" alt="Revenue grew 40% from Q1 to Q4"> - ❌
<img src="chart.png" alt="chart">
- ✅
-
Every form control has a programmatically associated label (
<label for>oraria-labelledby); errors linked viaaria-describedby+aria-invalid="true". -
Honor
prefers-reduced-motion: disable non-essential animation, parallax, and autoplay when set.
Workflow
- Inventory interactive elements and images on the page/component in scope.
- Apply rules 1–8, fixing violations directly in the markup/CSS (smallest diff that fixes the violation).
- Run automated checks if available (
npx axe-cli <url>or Lighthouse accessibility category); fix every reported violation. - Keyboard-only walkthrough: Tab through the whole flow — every control reachable, operable (Enter/Space/arrows), focus always visible, no traps.
- Screen-reader pass (VoiceOver: Cmd+F5 on macOS): headings/landmarks announce sensibly, images and controls have accessible names, errors are announced.
- Report remaining issues you cannot fix in code (e.g., brand color fails contrast) with the exact measured ratio and a compliant alternative.
Edge cases & failure modes
- Brand color fails contrast → do not silently change the brand; report the ratio (e.g., "3.2:1, needs 4.5:1") and propose the nearest compliant shade.
- Third-party widget is inaccessible → wrap with an accessible trigger where possible; otherwise flag it as a blocker, don't fake ARIA on top.
- Icon-only buttons → require
aria-label; a tooltip alone is not an accessible name. - Dynamic content updates (toasts, async results) → announce with
aria-live="polite"(orrole="alert"for errors only). - axe/Lighthouse unavailable → say so and rely on the manual walkthroughs; never claim "audit passed" on rules 1–8 alone.
References
Copy-paste patterns (skip links, focus styles, live regions, accessible modals, contrast tokens): see references/patterns.md