Web Accessibility (a11y)
Build accessible web apps - semantic HTML, ARIA roles, keyboard navigation, focus management, color contrast, and screen reader testing.
skill: Web Accessibility (a11y) category: Frontend tags: [Accessibility, ARIA, WCAG, Screen Reader, Keyboard Navigation] readingTime: 5
Accessibility is not an add-on - it's built into the HTML spec. Semantic HTML gets you 80% there automatically. ARIA is for when semantic HTML can't express the component's behavior (custom dropdowns, tabs, modals). The first rule of ARIA: don't use ARIA if a native HTML element does the job.
Workflow
- Use semantic HTML before ARIANative HTML elements have built-in accessibility - implicit ARIA roles, keyboard behavior, and screen reader announcements. Custom elements need all of this added manually.
- Add ARIA for custom componentsCustom dropdown menus, tabs, modals, and toggles need ARIA attributes to communicate state and role to assistive technologies.
- Manage focus for dynamic contentWhen content changes without a page reload (modal opens, route changes), focus must be moved programmatically - otherwise keyboard users are left behind.
- Ensure color contrast and visible focusWCAG 2.2 requires 4.5:1 contrast ratio for text. Low contrast is the most common accessibility failure. Focus indicators must be clearly visible for keyboard navigation.
- Test with automated tools and real screen readersAutomated tools catch 30-40% of accessibility issues. The rest requires real screen reader testing. Both are necessary.
Output Format
Accessibility: semantic landmarks, ARIA roles/states/properties, focus management with focus-visible, WCAG contrast ratios, and axe automated + screen reader testing.
HTML: header, nav, main, footer landmarks
ARIA: role + aria-label/labelledby + aria-expanded/selected
Focus: useEffect(() => ref.current.focus(), [isOpen])
CSS: :focus-visible outline, never outline:none
Test: jest-axe + VoiceOver/NVDA manual testingExamples
Input
Output
Edge Cases & Gotchas
- ARIA live region timing - Content must be injected into an existing aria-live element - inserting the element with content already inside doesn't trigger announcements in most screen readers.
- Images in links - A link containing only an image needs accessible text: <a href="/"><img alt="Home" src="logo.png"></a>. The img alt becomes the link's accessible name.
- Form error association - Error messages must be programmatically associated with their input via aria-describedby. Visual proximity is not enough for screen readers.
- Dynamic route changes - SPA route changes don't trigger screen reader page announcements like HTML page loads. Implement a "skip to main content" link and move focus to main heading on route change.
When to Break These Rules
- Never skip accessibility - it's a legal requirement in many jurisdictions (ADA, EN 301 549) and serves all users (captions help in noisy environments, keyboard nav helps power users).
- Don't add ARIA roles to HTML elements that already have the correct semantics - <button role="button"> is redundant and can cause screen reader confusion.
Reference Files
https://www.w3.org/WAI/ARIA/apg/https://webaim.org/