Building Accessible Web Applications: A Comprehensive Guide
Building accessible web applications isn't just about compliance—it's about creating better experiences for everyone. I've worked on accessibility retrofits and greenfield accessible projects, and the difference is night and day. Accessible applications are more usable for all users, not just those with disabilities.
Why accessibility matters
Accessibility (a11y) ensures that web applications work for people with disabilities. This includes:
- Visual impairments
- Motor disabilities
- Cognitive disabilities
- Hearing impairments
- Temporary disabilities (broken arm, etc.)
But accessibility benefits everyone. Good accessibility practices improve:
- SEO (search engines read semantic HTML)
- Mobile experience
- Performance (semantic HTML loads faster)
- Maintainability (clean, structured code)
WCAG guidelines overview
WCAG (Web Content Accessibility Guidelines) provides four principles:
- Perceivable - Information must be presentable in ways users can perceive
- Operable - Interface components must be operable
- Understandable - Information and operation must be understandable
- Robust - Content must be robust enough for assistive technologies
Semantic HTML foundation
Start with semantic HTML. Screen readers and other assistive technologies rely on proper semantic structure.
<!-- Good: Semantic structure -->
<header>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
</header>
<main>
<article>
<h1>Article Title</h1>
<p>Content here...</p>
</article>
</main>
<!-- Bad: Generic divs -->
<div class="header">
<div class="nav">
<div class="nav-list">
<div class="nav-item"><a href="/">Home</a></div>
</div>
</div>
</div>Keyboard navigation
Ensure all interactive elements are keyboard accessible. Users should be able to navigate using Tab, Enter, Space, and arrow keys.
// Custom dropdown with keyboard support
class AccessibleDropdown {
constructor(element) {
this.element = element;
this.button = element.querySelector('button');
this.menu = element.querySelector('[role="menu"]');
this.items = element.querySelectorAll('[role="menuitem"]');
this.setupEventListeners();
}
setupEventListeners() {
this.button.addEventListener('keydown', (e) => {
switch(e.key) {
case 'ArrowDown':
e.preventDefault();
this.openMenu();
this.focusFirstItem();
break;
case 'Escape':
this.closeMenu();
break;
}
});
}
}ARIA attributes
ARIA (Accessible Rich Internet Applications) attributes provide additional context for assistive technologies.
<!-- Progress indicator -->
<div role="progressbar"
aria-valuenow="75"
aria-valuemin="0"
aria-valuemax="100"
aria-label="Upload progress">
75%
</div>
<!-- Modal dialog -->
<div role="dialog"
aria-labelledby="dialog-title"
aria-describedby="dialog-description">
<h2 id="dialog-title">Confirm Action</h2>
<p id="dialog-description">Are you sure you want to delete this item?</p>
</div>Color contrast and visual design
Ensure sufficient color contrast:
- Normal text: 4.5:1 ratio
- Large text: 3:1 ratio
- UI components: 3:1 ratio
Don't rely on color alone to convey information. Use patterns, icons, or text labels.
Focus management
Clear focus indicators help users navigate. Avoid removing default focus outlines without replacement.
/* Good focus styles */
button:focus,
input:focus,
textarea:focus {
outline: 2px solid #007bff;
outline-offset: 2px;
}
/* Bad: Removing focus without replacement */
button:focus {
outline: none;
}Form accessibility
Forms need proper labeling and error handling:
<!-- Properly labeled form -->
<form>
<div>
<label for="email">Email Address</label>
<input type="email" id="email" name="email" required>
<div role="alert" class="error-message" style="display: none;">
Please enter a valid email address
</div>
</div>
</form>Images and media
Always provide alt text for images:
<!-- Decorative image --> <img src="decorative.png" alt=""> <!-- Informative image --> <img src="chart.png" alt="Sales growth chart showing 25% increase over 6 months"> <!-- Complex image --> <img src="diagram.png" alt="Database schema with users table connected to orders table via user_id foreign key">
Testing accessibility
Use these tools to test accessibility:
- Lighthouse - Automated testing
- axe-core - Developer tools integration
- NVDA/JAWS - Screen reader testing
- Keyboard-only navigation - Manual testing
Common mistakes to avoid
- Missing alt text - Screen readers can't describe images
- Poor color contrast - Content becomes unreadable
- Missing form labels - Users don't know what to enter
- Non-keyboard accessible controls - Many users can't use a mouse
- Auto-playing media - Can be distracting and inaccessible
Performance and accessibility
Accessible sites often perform better because:
- Semantic HTML loads faster
- Fewer DOM manipulations needed
- Better caching strategies possible
Legal considerations
In many countries, web accessibility is legally required for government websites and often for commercial sites serving the public. WCAG compliance can protect against lawsuits.
Tools and resources
- Lighthouse - Built into Chrome DevTools
- WAVE - Web accessibility evaluation tool
- axe DevTools - Browser extension
- Color Contrast Analyzer - For checking contrast ratios
- Screen reader testing - NVDA (free), JAWS
Building accessibility culture
Make accessibility part of your development process:
- Include accessibility in design reviews
- Test with assistive technologies regularly
- Train team members on accessibility basics
- Set accessibility goals and metrics
Measuring success
Track these accessibility metrics:
- Lighthouse accessibility score
- Manual testing completion rate
- User feedback from assistive technology users
- Compliance with WCAG guidelines
Building accessible applications takes effort, but the result is a better experience for everyone. Start small—focus on semantic HTML and keyboard navigation first, then build from there.
Related articles