Accessibility Best Practices: Building Inclusive Experiences
Creating websites that work for everyone
Web accessibility isn't just a legal requirement—it's a moral imperative and good business practice. Over 1 billion people worldwide have disabilities. Building accessible sites expands your audience, improves SEO, and creates better experiences for everyone.
Understanding WCAG Guidelines
The Web Content Accessibility Guidelines (WCAG) define accessibility through four principles—POUR:
- Perceivable: Information must be presentable to users in ways they can perceive
- Operable: Interface components must be operable by all users
- Understandable: Information and operation must be understandable
- Robust: Content must work with current and future technologies
WCAG has three conformance levels: A (minimum), AA (recommended), and AAA (enhanced). Target AA compliance for most projects.
Semantic HTML: The Foundation
Semantic elements provide meaning that assistive technologies understand:
<header>
<nav aria-label="Main navigation">
<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>
<footer>
<p>Copyright information</p>
</footer>
ARIA: Enhancing Accessibility
Accessible Rich Internet Applications (ARIA) attributes provide additional context when semantic HTML isn't enough. Use ARIA sparingly—native HTML is always preferred when available.
Common ARIA attributes:
aria-label: Provides accessible name when visible
label doesn't exist:
<button aria-label="Close dialog">
<span aria-hidden="true">×</span>
</button>
aria-labelledby: References another element for
the label:
<h2 id="dialog-title">Confirm Action</h2>
<div role="dialog" aria-labelledby="dialog-title">
<!-- dialog content -->
</div>
aria-describedby: Provides additional
description:
<input
type="password"
aria-describedby="password-requirements"
>
<span id="password-requirements">
Must be at least 8 characters
</span>
aria-live: Announces dynamic content changes:
<div aria-live="polite" aria-atomic="true">
Items added to cart: 3
</div>
Keyboard Navigation: Essential Accessibility
All functionality must be accessible via keyboard. Many users rely exclusively on keyboard navigation due to motor disabilities or preference.
Key requirements:
- All interactive elements must be focusable with Tab
- Focus order should follow logical reading order
- Focus indicator must be clearly visible
- No keyboard traps (users can tab out of all elements)
Focus styling:
/* Never remove focus outline without replacement */
button:focus {
outline: 3px solid #0066cc;
outline-offset: 2px;
}
/* Or use modern focus-visible */
button:focus-visible {
outline: 3px solid #0066cc;
outline-offset: 2px;
}
Custom interactive elements need keyboard support:
<div
role="button"
tabindex="0"
onclick="handleClick()"
onkeydown="if(event.key === 'Enter' || event.key === ' ') handleClick()"
>
Custom Button
</div>
Color Contrast Requirements
WCAG AA requires minimum contrast ratios:
- Normal text: 4.5:1
- Large text (18pt+ or 14pt+ bold): 3:1
- UI components and graphics: 3:1
Use tools like WebAIM Contrast Checker or browser DevTools to verify contrast. Never rely on color alone to convey information:
<!-- Bad: color only -->
<span style="color: red;">Error</span>
<!-- Good: color plus icon/text -->
<span style="color: red;">
<span aria-hidden="true">⚠️</span>
Error: Invalid email address
</span>
Forms and Input Accessibility
Forms are critical interaction points that must be fully accessible:
<form>
<label for="email">Email Address</label>
<input
type="email"
id="email"
name="email"
required
aria-required="true"
aria-describedby="email-help"
>
<span id="email-help">
We'll never share your email
</span>
<!-- Error state -->
<input
type="email"
id="email"
aria-invalid="true"
aria-describedby="email-error"
>
<span id="email-error" role="alert">
Please enter a valid email address
</span>
</form>
Best practices:
-
Always associate labels with inputs using
forattribute -
Group related inputs with
<fieldset>and<legend> - Provide clear error messages associated with fields
-
Use appropriate input types (
email,tel,date) - Include autocomplete attributes for common fields
Alternative Text for Images
Every image needs alt text—but the content depends on context:
<!-- Informative image -->
<img src="chart.png" alt="Sales increased 25% in Q4">
<!-- Decorative image -->
<img src="decoration.png" alt="" role="presentation">
<!-- Functional image (button/link) -->
<a href="/search">
<img src="search.png" alt="Search">
</a>
<!-- Complex image -->
<img
src="complex-chart.png"
alt="Quarterly sales data"
aria-describedby="chart-description"
>
<div id="chart-description">
Detailed description of chart data...
</div>
Skip Links: Navigation Shortcuts
Skip links allow keyboard users to bypass repetitive navigation:
<a href="#main-content" class="skip-link">
Skip to main content
</a>
<!-- CSS -->
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #000;
color: white;
padding: 8px;
z-index: 100;
}
.skip-link:focus {
top: 0;
}
Testing for Accessibility
Regular testing is essential:
- Automated tools: Lighthouse, axe DevTools, WAVE
- Keyboard testing: Navigate entire site with Tab key only
- Screen readers: Test with NVDA (Windows), JAWS, or VoiceOver (Mac/iOS)
- Browser zoom: Test at 200% zoom minimum
- Color blindness: Use simulators to check for color-only information
Common Mistakes to Avoid
- Removing focus outlines without replacement
-
Using
divorspanas buttons instead of<button> - Forgetting alt text or writing useless alt text ("image", "photo")
- Creating keyboard traps in modals or dropdowns
- Using placeholder as label replacement
- Setting font sizes in pixels instead of relative units
Accessibility benefits everyone—keyboard users navigate faster, captions help in noisy environments, and clear interfaces reduce cognitive load. Building accessible sites creates better experiences for all users while expanding your reach.