Web Accessibility: A Developer's Guide
Maria Garcia
Web Accessibility: A Developer's Guide
Web accessibility ensures that websites and applications are usable by everyone, including people with disabilities. It's not just about compliance—it's about creating inclusive experiences that benefit all users.
Why Accessibility Matters
The Numbers
- 1 billion people worldwide live with some form of disability
- 15% of the global population experiences some form of disability
- $13 trillion in annual disposable income controlled by people with disabilities
Legal Requirements
- ADA: Americans with Disabilities Act
- Section 508: US federal accessibility requirements
- WCAG: Web Content Accessibility Guidelines (international standard)
- EN 301 549: European accessibility standard
WCAG 2.1 Principles
1. Perceivable
Information must be presentable in ways users can perceive:
<!-- Good: Alt text for images -->
<img src="chart.png" alt="Sales increased 25% from Q1 to Q2" />
<!-- Good: Captions for videos -->
<video controls>
<source src="video.mp4" type="video/mp4" />
<track kind="captions" src="captions.vtt" srclang="en" label="English" />
</video>
2. Operable
Interface components must be operable:
<!-- Good: Keyboard accessible -->
<button onclick="toggleMenu()" onkeydown="handleKeyDown(event)">
Menu
</button>
<!-- Good: Focus management -->
<div tabindex="0" role="button" aria-pressed="false">
Custom Button
</div>
3. Understandable
Information and UI operation must be understandable:
<!-- Good: Clear labels -->
<label for="email">Email Address (required)</label>
<input type="email" id="email" required aria-describedby="email-error" />
<div id="email-error" role="alert">Please enter a valid email address</div>
4. Robust
Content must be robust enough for various assistive technologies:
<!-- Good: Semantic HTML -->
<nav aria-label="Main navigation">
<ul>
<li><a href="/" aria-current="page">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
Common Accessibility Issues
1. Missing Alt Text
<!-- Bad -->
<img src="product.jpg" />
<!-- Good -->
<img src="product.jpg" alt="Red leather handbag with gold hardware" />
<!-- Decorative images -->
<img src="decoration.jpg" alt="" role="presentation" />
2. Poor Color Contrast
/* Bad: Insufficient contrast */
.text {
color: #999;
background: #fff; /* Contrast ratio: 2.85:1 */
}
/* Good: Sufficient contrast */
.text {
color: #666;
background: #fff; /* Contrast ratio: 5.74:1 */
}
3. Keyboard Navigation Issues
// Bad: Not keyboard accessible
<div onClick={handleClick}>Click me</div>
// Good: Keyboard accessible
<button onClick={handleClick} onKeyDown={handleKeyDown}>
Click me
</button>
// Good: Custom interactive element
<div
role="button"
tabIndex={0}
onClick={handleClick}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
handleClick();
}
}}
>
Custom Button
</div>
ARIA (Accessible Rich Internet Applications)
Roles
<div role="alert">Error: Please fill in all required fields</div>
<div role="tabpanel" aria-labelledby="tab1">Tab content</div>
<div role="progressbar" aria-valuenow="32" aria-valuemin="0" aria-valuemax="100">
32% complete
</div>
Properties
<button aria-expanded="false" aria-controls="menu">Menu</button>
<input aria-required="true" aria-describedby="password-help" />
<div id="password-help">Password must be at least 8 characters</div>
States
<button aria-pressed="true">Bold</button>
<input aria-invalid="true" aria-describedby="error" />
<div aria-hidden="true">Decorative content</div>
Testing for Accessibility
Automated Testing
// Using axe-core
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
test('should not have accessibility violations', async () => {
const { container } = render(<MyComponent />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
Manual Testing
- Keyboard Navigation: Tab through all interactive elements
- Screen Reader: Test with NVDA, JAWS, or VoiceOver
- Color Contrast: Use tools like WebAIM's contrast checker
- Zoom: Test at 200% zoom level
- Focus Management: Ensure focus is visible and logical
Tools
- axe DevTools: Browser extension for accessibility testing
- Lighthouse: Built-in Chrome accessibility audit
- WAVE: Web accessibility evaluation tool
- Color Oracle: Color blindness simulator
React Accessibility Patterns
Focus Management
import { useRef, useEffect } from 'react';
function Modal({ isOpen, onClose, children }) {
const modalRef = useRef(null);
const previousFocus = useRef(null);
useEffect(() => {
if (isOpen) {
previousFocus.current = document.activeElement;
modalRef.current?.focus();
} else {
previousFocus.current?.focus();
}
}, [isOpen]);
if (!isOpen) return null;
return (
<div
ref={modalRef}
role="dialog"
aria-modal="true"
tabIndex={-1}
onKeyDown={(e) => {
if (e.key === 'Escape') onClose();
}}
>
{children}
</div>
);
}
Form Accessibility
function AccessibleForm() {
const [errors, setErrors] = useState({});
return (
<form>
<fieldset>
<legend>Personal Information</legend>
<div>
<label htmlFor="firstName">
First Name {errors.firstName && <span aria-label="required">*</span>}
</label>
<input
id="firstName"
type="text"
required
aria-invalid={!!errors.firstName}
aria-describedby={errors.firstName ? "firstName-error" : undefined}
/>
{errors.firstName && (
<div id="firstName-error" role="alert">
{errors.firstName}
</div>
)}
</div>
</fieldset>
</form>
);
}
Performance and Accessibility
Reduced Motion
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
High Contrast Mode
@media (prefers-contrast: high) {
.button {
border: 2px solid;
background: ButtonFace;
color: ButtonText;
}
}
Building an Accessibility Culture
Team Practices
- Include accessibility in design reviews
- Add accessibility testing to CI/CD
- Provide accessibility training
- Use accessibility linting tools
- Test with real users
Documentation
- Document accessibility features
- Maintain an accessibility checklist
- Create component accessibility guidelines
- Share accessibility resources
Conclusion
Web accessibility is not a feature—it's a fundamental aspect of good web development. By following WCAG guidelines, using semantic HTML, implementing ARIA correctly, and testing thoroughly, we can create web experiences that truly work for everyone.
Accessibility benefits everyone: what helps users with disabilities often improves the experience for all users.