Accessibility Checklist
Accessibility review and fixes reference. Read this when auditing or building accessible frontend interfaces.
Labels
- Every
<input>,<select>, and<textarea>has a visible<label>with matchingfor/id. - Icon-only buttons have
aria-labeldescribing the action (e.g.,aria-label="Close dialog"). - Images have meaningful
alttext. Decorative images usealt="". - Form groups use
<fieldset>+<legend>for related inputs (e.g., radio groups, address fields). - Placeholder text is NOT a substitute for labels.
Keyboard Navigation
- All interactive elements reachable via Tab key.
- Tab order follows visual reading order (top-left to bottom-right, top to bottom).
- Custom components (dropdowns, date pickers, sliders) support expected keyboard patterns:
- Dropdowns: Arrow keys to navigate, Enter/Space to select, Escape to close.
- Tabs: Arrow keys to switch tabs, Tab key moves into panel content.
- Modals: Tab traps focus inside. Escape closes modal.
- No keyboard traps — user can always Tab out of any component.
- Skip-to-content link as first focusable element on the page.
Focus Management
- Focus ring visible on all interactive elements — never set
outline: nonewithout replacement. - Use
focus-visiblefor keyboard-only focus indicators (avoid showing focus ring on mouse click). - After modal close, return focus to the trigger element.
- After inline deletion, move focus to next logical item.
- After route change in SPA, move focus to main content or page heading.
:focus-visible {
outline: 2px solid var(--focus-color);
outline-offset: 2px;
}Contrast
- Body text: 4.5:1 contrast ratio minimum (WCAG AA).
- Large text (18px+ regular, 14px+ bold): 3:1 minimum.
- UI controls and graphical elements: 3:1 against adjacent colors.
- Check contrast in both light and dark mode.
- Test with browser DevTools (Lighthouse accessibility audit or contrast checker).
Reduced Motion
- Wrap animations and transitions in
prefers-reduced-motionmedia query. - Essential motion (e.g., loading spinners) can remain but should be subtle.
- Parallax effects, auto-playing carousels, and decorative animations should be disabled.
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}Semantic HTML
- Use
<nav>for navigation,<main>for primary content,<aside>for secondary content,<footer>for page footer. - Use
<button>for actions,<a>for navigation. Never use<div>or<span>as clickable elements. - Use heading levels (
h1–h6) in hierarchical order. One<h1>per page. - Use
<ul>/<ol>for lists — screen readers announce item count. - Use
<table>with<th>andscopefor data tables — not for layout. - Use
<dialog>for modals when possible (native focus trap and Escape key).
ARIA Restraint
ARIA is a last resort. Prefer native HTML elements that provide semantics for free.
- Do not add ARIA roles that duplicate native semantics —
<button role="button">is redundant. - Do use ARIA when native HTML cannot express the pattern:
aria-expandedon toggles that reveal content.aria-controlslinking a toggle to the panel it controls.aria-live="polite"for dynamic status messages (e.g., "3 results found").role="alert"for urgent error messages.aria-describedbyto link form inputs to help text or error messages.
- Never use ARIA to make non-semantic elements interactive — use
<button>,<a>,<input>instead. - Test with a screen reader (VoiceOver, NVDA, or JAWS) when possible. ARIA mistakes are silent errors.
Quick Audit Process
- Tab through entire page — verify all interactive elements reachable, focus visible.
- Run Lighthouse accessibility audit — fix any issues flagged.
- Check color contrast with DevTools or browser extension.
- Test with
prefers-reduced-motion: reduceenabled. - Navigate with screen reader (optional but recommended for complex UIs).
- Verify all form fields have labels and error messages are announced.