Accessibility Fundamentals
The Web Is for Everyone
Accessibility (often abbreviated a11y) means building websites that people with disabilities can use. This includes people who are blind, deaf, have motor impairments, cognitive disabilities, or temporary situational limitations (a broken arm, bright sunlight on a screen, a loud environment).
About 15% of the world's population lives with some form of disability. Accessibility is not a niche concern -- it is a fundamental quality of well-built software.
WCAG: The Standard
The Web Content Accessibility Guidelines (WCAG) are the international standard for web accessibility, published by the W3C. WCAG is organized around four principles (POUR):
- Perceivable: Users can perceive the content (see it, hear it, or read it)
- Operable: Users can interact with the interface (click, type, navigate)
- Understandable: Users can understand the content and interface
- Robust: Content works across assistive technologies
Conformance Levels
| Level | Meaning | Target |
|---|---|---|
| A | Minimum accessibility | Bare minimum, many barriers remain |
| AA | Standard accessibility | The target for most websites and legal requirements |
| AAA | Enhanced accessibility | Highest level, not always achievable for all content |
Target AA for all projects. It covers the critical requirements: color contrast, keyboard access, screen reader compatibility, and more.
Alt Text for Images
Every <img> element needs an alt attribute. The alt text serves as a replacement when the image cannot be seen -- by screen readers, when images fail to load, or when users have images disabled.
<!-- Informative image: describe what it shows -->
<img src="chart.png" alt="Bar chart showing revenue growth from $2M in 2023 to $5M in 2025">
<!-- Functional image (used as a link or button): describe what it does -->
<a href="/home">
<img src="logo.png" alt="Acme Corp home page">
</a>
<!-- Decorative image: empty alt (not missing, empty) -->
<img src="decorative-wave.svg" alt="">
<!-- Complex image: brief alt with longer description nearby -->
<figure>
<img src="architecture.png" alt="System architecture overview">
<figcaption>
The system consists of three tiers: a React frontend communicating
with a Node.js API layer, backed by PostgreSQL for persistence
and Redis for caching.
</figcaption>
</figure>
Alt Text Rules
- Never start with "image of" or "picture of" -- the screen reader already announces it as an image
- Be concise -- one or two sentences at most
- Be specific -- "a dog" is less useful than "a golden retriever puppy sleeping on a couch"
- Empty
alt=""is correct for decorative images -- it tells screen readers to skip the image entirely - Missing
altattribute (no attribute at all) causes screen readers to read the filename, which is useless
Keyboard Navigation
Many users navigate entirely with a keyboard: people with motor impairments, power users, and anyone whose mouse breaks. Every interactive element must be reachable and operable with the keyboard.
The Tab Order
Pressing Tab moves focus through interactive elements in document order:
<!-- These are focusable by default -->
<a href="/about">About</a> <!-- Tab stop 1 -->
<button>Click me</button> <!-- Tab stop 2 -->
<input type="text" name="search"> <!-- Tab stop 3 -->
<select name="sort">...</select> <!-- Tab stop 4 -->
<textarea name="comment"></textarea> <!-- Tab stop 5 -->
Non-interactive elements (<div>, <span>, <p>) are not in the tab order by default. This is correct. Do not make everything focusable.
Making Custom Elements Focusable
When you build custom interactive components, use tabindex:
<!-- Add to tab order (value 0 = in normal document order) -->
<div role="button" tabindex="0" onclick="handleClick()">
Custom Button
</div>
<!-- Focusable but NOT in tab order (for programmatic focus) -->
<div id="modal" tabindex="-1">
Modal content...
</div>
Never use tabindex greater than 0. It overrides the natural tab order and creates confusion.
Keyboard Interaction Patterns
// Custom button must respond to Enter and Space
element.addEventListener('keydown', (event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
handleActivation();
}
});
Better yet, use a <button> element and get this for free.
Focus Management
Focus must be visible and must follow user expectations.
Visible Focus Indicators
/* Never do this without a replacement */
/* :focus { outline: none; } */
/* Modern approach: visible focus for keyboard, hidden for mouse */
:focus-visible {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
:focus-visible only shows the focus indicator when the user is navigating with a keyboard, not when clicking with a mouse.
When content changes dynamically (modals, route changes), move focus to the new content and return it when dismissed.
Skip Links
A skip link lets keyboard users jump past repeated navigation to the main content:
<body>
<a href="#main-content" class="skip-link">Skip to main content</a>
<nav>
<!-- 20 navigation links -->
</nav>
<main id="main-content" tabindex="-1">
<!-- Page content -->
</main>
</body>
Style the skip link with position: absolute; top: -100% and reveal it on :focus with top: 0.
Color Contrast
Text must have sufficient contrast against its background. WCAG AA requires:
| Text Size | Minimum Contrast Ratio |
|---|---|
| Normal text (under 18px or under 14px bold) | 4.5:1 |
| Large text (18px+ or 14px+ bold) | 3:1 |
| UI components and graphical objects | 3:1 |
/* Passes AA: white on dark blue = 8.59:1 */
.good-contrast {
color: #ffffff;
background-color: #1e3a5f;
}
/* Fails AA: light gray on white = 2.14:1 */
.bad-contrast {
color: #b0b0b0;
background-color: #ffffff;
}
Never convey information through color alone. A red/green color scheme for error/success fails for colorblind users. Add icons, text, or patterns:
<!-- Bad: color only -->
<span style="color: red">Error</span>
<!-- Good: color + icon + text -->
<span class="error" role="alert">
<svg aria-hidden="true"><!-- error icon --></svg>
Error: Email address is required
</span>
ARIA: Accessible Rich Internet Applications
ARIA attributes add semantic information when HTML alone is insufficient. ARIA has three categories: roles, states, and properties.
Roles
<!-- Landmark roles (prefer semantic HTML instead) -->
<div role="navigation">...</div> <!-- Use <nav> instead -->
<div role="main">...</div> <!-- Use <main> instead -->
<!-- Widget roles (for custom components) -->
<div role="tablist">
<button role="tab" aria-selected="true">Tab 1</button>
<button role="tab" aria-selected="false">Tab 2</button>
</div>
<div role="tabpanel">Tab 1 content</div>
<!-- Alert role (announces content to screen readers) -->
<div role="alert">Form submitted successfully.</div>
States & Properties
<!-- Expanded/collapsed -->
<button aria-expanded="false" aria-controls="menu">Menu</button>
<ul id="menu" hidden>...</ul>
<!-- Label for elements without visible text -->
<button aria-label="Close">X</button>
<!-- Described by another element -->
<input type="password" aria-describedby="pw-requirements">
<p id="pw-requirements">Must be at least 8 characters with one number.</p>
<!-- Live regions (announce dynamic changes) -->
<div aria-live="polite">
<!-- Screen reader announces changes here without interrupting -->
3 items in your cart
</div>
<!-- Hidden from screen readers -->
<svg aria-hidden="true"><!-- decorative icon --></svg>
The First Rule of ARIA
If you can use a native HTML element with the semantics and behavior you require, use it instead of adding ARIA.
A <button> is always better than <div role="button" tabindex="0">. Native elements have built-in keyboard handling, focus management, and screen reader support.
Testing Accessibility
Automated Testing
Automated tools catch about 30-40% of accessibility issues. They are a starting point, not a complete solution.
Lighthouse (built into Chrome DevTools):
- Open DevTools
- Go to the Lighthouse tab
- Select "Accessibility"
- Click "Analyze page load"
axe DevTools (browser extension):
- Install the axe DevTools extension
- Open DevTools
- Go to the axe tab
- Click "Scan ALL of my page"
Keyboard Testing
- Put your mouse in a drawer
- Navigate the entire page with Tab, Shift+Tab, Enter, Space, Escape, and arrow keys
- Check: Can you reach every interactive element? Can you see where focus is? Can you operate every control? Can you escape from modals and dropdowns?
Screen Reader Testing
Every platform has a built-in screen reader: VoiceOver on macOS/iOS (Cmd+F5), TalkBack on Android, and Narrator on Windows. NVDA is a free, full-featured screen reader for Windows. Test with at least one.
The Legal & Ethical Case
Accessibility is increasingly a legal requirement. In the US, the ADA (Americans with Disabilities Act) has been interpreted to apply to websites. The EU's European Accessibility Act requires accessible products and services. Lawsuits over inaccessible websites are common and increasing.
Beyond legal compliance: accessibility is a quality issue. Accessible websites are better for everyone. Captions help people in noisy environments. Keyboard navigation helps power users. Good contrast helps people in bright sunlight. Semantic HTML helps search engines. Building accessibly is building well.
Common Pitfalls
- Missing alt attributes on images: Screen readers read the filename if
altis missing entirely. Always includealt, even if it is empty (alt=""for decorative images). - Removing focus outlines without replacement:
outline: nonemakes keyboard navigation impossible. Use:focus-visiblefor a better experience. - Using ARIA when HTML would suffice: Adding
role="button"to a<div>when you could use a<button>. Native elements work better. - Color as the only indicator: Red text for errors, green for success -- this fails for colorblind users. Add icons, labels, or patterns.
- Mouse-only interactions: Hover tooltips, drag-and-drop without keyboard alternatives, click handlers on non-interactive elements.
- Ignoring focus management in SPAs: When the page content changes without a full reload, screen readers do not know. Move focus to the new content or announce changes with
aria-live. - Only testing with automated tools: Automated tools miss most usability issues. Keyboard testing and screen reader testing are essential.
Key Takeaways
- Accessibility benefits everyone, not just people with permanent disabilities
- Target WCAG AA conformance: color contrast, keyboard access, screen reader support, clear content
- Every image needs
alttext -- informative images get descriptions, decorative images getalt="" - Every interactive element must be keyboard accessible with visible focus indicators
- Never convey information through color alone
- Use native HTML elements before reaching for ARIA
- Test with automated tools (Lighthouse, axe), keyboard navigation, and at least one screen reader
- Accessibility is a legal requirement in many jurisdictions and increasingly enforced