Web Accessibility Guide
Over 1 billion people have disabilities. Make your websites work for everyone.
Why Accessibility Matters
- Legal requirement in many countries
- Better SEO - accessibility improves search rankings
- Larger audience - reach more users
- Better UX - everyone benefits
Semantic HTML
Use the right elements:
<!-- Bad -->
<div class="button" onclick="submit()">Submit</div>
<!-- Good -->
<button type="submit">Submit</button>
<!-- Bad -->
<div class="header">
<div class="nav">...</div>
</div>
<!-- Good -->
<header>
<nav>...</nav>
</header>
Keyboard Navigation
Everything should work without a mouse:
/* Never hide focus indicators completely */
button:focus {
outline: 2px solid #0066cc;
outline-offset: 2px;
}
/* Skip link for keyboard users */
.skip-link {
position: absolute;
top: -40px;
}
.skip-link:focus {
top: 0;
}
<a href="#main-content" class="skip-link">Skip to main content</a>
Images and Alt Text
<!-- Informative images -->
<img src="chart.png" alt="Sales increased 50% from Q1 to Q2">
<!-- Decorative images -->
<img src="decoration.png" alt="" role="presentation">
<!-- Complex images -->
<figure>
<img src="infographic.png" alt="Company growth infographic">
<figcaption>
Detailed description of the infographic...
</figcaption>
</figure>
ARIA Attributes
When HTML isn't enough:
<!-- Live regions for dynamic content -->
<div aria-live="polite" aria-atomic="true">
3 items in cart
</div>
<!-- Custom components -->
<div role="tablist">
<button role="tab" aria-selected="true" aria-controls="panel1">Tab 1</button>
<button role="tab" aria-selected="false" aria-controls="panel2">Tab 2</button>
</div>
<div role="tabpanel" id="panel1">Content 1</div>
<div role="tabpanel" id="panel2" hidden>Content 2</div>
<!-- Form validation -->
<input
type="email"
aria-invalid="true"
aria-describedby="email-error"
>
<span id="email-error">Please enter a valid email</span>
Color and Contrast
/* Minimum contrast ratio: 4.5:1 for normal text */
.text {
color: #333; /* On white background */
background: #fff;
}
/* Don't rely on color alone */
.error {
color: #d32f2f;
border-left: 4px solid #d32f2f;
}
.error::before {
content: "⚠ ";
}
Forms
<form>
<label for="email">Email address</label>
<input
type="email"
id="email"
name="email"
required
aria-required="true"
>
<fieldset>
<legend>Notification preferences</legend>
<label>
<input type="checkbox" name="email-notifications">
Email notifications
</label>
</fieldset>
</form>
Testing Tools
- axe DevTools - Browser extension
- WAVE - Web accessibility evaluator
- Lighthouse - Built into Chrome
- Screen readers - NVDA, VoiceOver
Conclusion
Accessibility isn't optional. Start with semantic HTML, test with real users, and continuously improve.