Accessibility as Design
Accessibility is not a feature you bolt on after launch — it's a design constraint that improves the experience for everyone. Screen reader users, keyboard navigators, and users with color vision deficiencies are the most obvious beneficiaries. But accessible design also helps users on slow connections, in bright sunlight, with broken trackpads, or using your app one-handed on a bus.
Accessible design IS good design. Every technique on this page benefits all users, not just users with disabilities.
Lighthouse's Accessibility audit (powered by axe-core) is a fast automated complement to the manual checks on this page — see Performance & Core Web Vitals for how to run it in DevTools, CI, and against preview URLs.
The Overlap
This is the key insight. Every accessibility practice has a wider audience than you might expect:
| Accessibility Practice | Who It Also Benefits |
|---|---|
| Color contrast ≥ 4.5:1 | Users in bright sunlight, cheap monitors, aging eyes |
| Keyboard navigation | Power users, broken trackpad/mouse, terminal-oriented devs |
| Touch targets ≥ 44px | All mobile users, especially one-handed or on-the-go |
prefers-reduced-motion | Low-end devices, motion-sensitive users, battery saving |
| Semantic HTML | SEO crawlers, reader mode, LLM parsing, future-proofing |
| Visible focus indicators | Keyboard users, tab navigators, accessibility audits |
| Alt text on images | Slow connections (image not loaded), SEO, link previews |
| Captions on video | Noisy environments, non-native speakers, searchability |
Color & Contrast
Minimum Contrast Ratios
WCAG defines minimum contrast ratios for readable, accessible text. See Visual Design Fundamentals for the full contrast ratio table and color palette guidance. The short version:
- 4.5:1 — normal text (below 18pt)
- 3:1 — large text (18pt+ or 14pt+ bold) and UI components
- 7:1 — enhanced (Level AAA)
Pure black on pure white (21:1) is technically fine but can feel harsh. Near-black (#1a1a1a) on near-white (#fafafa) is gentler and still well above the minimum.
Don't Rely on Color Alone
Never use color as the only way to convey meaning. About 8% of males have some form of color vision deficiency — red and green are the most commonly confused pair.
- Bad: Status shown only with red/green dots.
- Good: Red dot + ✕ icon + "Failed" text label. Green dot + ✓ icon + "Passed" text label.
Always pair color with text, icons, or patterns so the meaning is clear regardless of how colors are perceived.
Testing Contrast
- Chrome DevTools: Inspect an element → click the color swatch in the Styles panel → the color picker shows the contrast ratio against the background.
- Chrome DevTools: Open the Rendering tab → "Emulate vision deficiencies" to simulate protanopia, deuteranopia, tritanopia, and achromatopsia.
- See Design Tools & Resources for standalone contrast checker tools.
Focus Management
Visible Focus Indicators
Browsers provide a default focus outline that works. Unfortunately, it's often removed with outline: none for aesthetic reasons. If you remove the default, you must replace it with a custom visible focus style — otherwise keyboard users cannot see where they are on the page.
:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
:focus-visible
:focus-visible shows the focus ring for keyboard navigation but not for mouse clicks. This is the correct balance — keyboard users see focus, mouse users don't get visual noise.
- Use
:focus-visibleinstead of:focusfor styling in all cases. - Supported in all modern browsers.
- The browser heuristic handles the decision automatically: keyboard interaction triggers it, mouse clicks do not.
Focus Order
tabindex="0"— adds the element to the natural tab order (follows DOM position).tabindex="-1"— makes the element focusable programmatically (via JavaScript) but not reachable via the Tab key.- Never use positive
tabindexvalues (tabindex="1","2", etc.) — this overrides DOM order and creates an unpredictable, chaotic tab sequence. Fix the DOM order instead.
Focus Trapping in Modals
When a modal opens, trap focus inside it — pressing Tab should cycle through only the modal's interactive elements, not escape to the page behind it. When the modal closes, return focus to the element that triggered it.
- Use the native
<dialog>element — it has built-in focus trapping and Escape-to-close. - Alternatively, use the
inertattribute on background content to prevent interaction with it. - Libraries like Radix UI and Headless UI handle focus trapping automatically.
- shadcn/ui is built on Radix and inherits its focus management.
Touch & Click Targets
Minimum Sizes
Multiple standards converge on similar minimums:
- Apple HIG: 44×44pt
- Google Material Design: 48×48dp
- WCAG 2.2 Level AA (SC 2.5.8): 24×24 CSS px minimum
Practical recommendation: aim for 44–48px. Never go below 24px.
button, a, [role="button"] {
min-height: 44px;
min-width: 44px;
padding: 0.5rem 1rem;
}
Spacing Between Targets
- Maintain at least 8px between adjacent interactive elements.
- This is especially critical when destructive actions sit next to safe ones — "Delete" right next to "Edit" with no gap is an accident waiting to happen.
- Inline links within paragraphs should have enough padding to be visually and physically distinguishable as tap targets.
Motion & Animation
prefers-reduced-motion
Some users have vestibular disorders, migraines, or seizure conditions that are triggered by motion. Respect their preference with prefers-reduced-motion.
The recommended approach is to opt in to motion rather than opting out — start with no motion and only add it if the user hasn't indicated a preference:
/* Base: no motion */
.element {
transition: none;
}
/* Only add motion if user hasn't set a reduce preference */
@media (prefers-reduced-motion: no-preference) {
.element {
transition: transform 200ms ease;
}
}
In JavaScript, check the preference with:
const prefersReducedMotion =
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
What to Reduce
Not all motion is equal. The goal is removing vestibular triggers, not eliminating all visual feedback:
- Eliminate: parallax scrolling, auto-playing carousels, large-scale page transitions, background video.
- Keep: opacity fades, subtle color transitions, focus ring animations — these don't trigger vestibular issues.
Semantic HTML
Why Semantics Matter
Screen readers use landmark elements (<nav>, <main>, <aside>) to let users jump between sections of a page. A <button> gets keyboard activation (Enter and Space) for free — a <div> does not. <form> enables Enter-to-submit. <details> gets native toggle behavior.
Semantic HTML means less JavaScript and better accessibility at zero cost.
A <div> with an onclick handler is not a button. It has no keyboard support, no focus, no ARIA role, and no screen reader announcement. Use <button> for actions and <a> for navigation — always.
Common Mistakes
| Instead of | Use |
|---|---|
<div onclick="..."> | <button> |
<div class="header"> | <header> |
<span class="link"> | <a href="..."> |
<div class="list"><div>Item</div></div> | <ul><li>Item</li></ul> |
<b> for emphasis | <strong> |
Generic <div> wrappers | <main>, <nav>, <aside>, <section>, <article> |
Landmark Roles
Structure your page with landmarks so screen reader users can navigate efficiently:
<header>— site or section header<nav>— navigation links<main>— primary content (one per page)<aside>— complementary content (sidebars, related links)<footer>— site or section footer
Screen readers can jump directly between landmarks. Structure your page so this makes sense — if a screen reader user skips from <nav> to <main> to <footer>, they should land in logical places.
Quick Wins Checklist
The highest-impact, lowest-effort accessibility improvements. If you do nothing else, do these:
- Add
langattribute to<html>(e.g.,<html lang="en">) - Use semantic HTML elements (
<button>,<a>,<nav>,<main>) - Ensure all images have
alttext (usealt=""for purely decorative images) - Check text contrast ratios — minimum 4.5:1
- Add
:focus-visiblestyles to all interactive elements - Make all interactive elements keyboard-accessible
- Use
<button>for actions,<a>for navigation - Add
prefers-reduced-motionsupport to animations - Ensure touch targets are at least 44×44px
- Test with keyboard only — unplug your mouse for 5 minutes
This page covers the design side of accessibility. For comprehensive WCAG coverage, see the WCAG 2.2 Quick Reference and web.dev Learn Accessibility. For additional resources, see Web — Accessibility.
Further Reading
- Visual Design Fundamentals — Contrast ratios, color semantics, and spacing guidelines.
- Common UI Patterns — Accessible navigation, form validation, and touch targets in context.
- Design Tools & Resources — Contrast checkers, DevTools guides, and icon libraries.
- WCAG 2.2 Quick Reference — The complete standard with filtering by level and topic.
- web.dev Learn Accessibility — Free structured course from the Chrome team.
- The A11y Project — Practical accessibility checklist and guides.
- Inclusive Components — Patterns for building accessible UI components.