The Ultimate Guide to Web Accessibility
May 20, 2025
TL;DR Accessibility is not a checkbox—it’s an ongoing quality practice that benefits every user.
This guide gives you a soup-to-nuts roadmap for building, testing, and maintaining accessible web experiences in 2025 and beyond.
Each chapter links to deep-dive articles for hands-on samples and advanced techniques.
Table of Contents
- Why Accessibility Matters
- Regulations & Standards
- Semantic Foundations
- Keyboard Navigation & Focus
- Visual Design & Color Contrast
- Rich Media & Motion
- Forms & Error Handling
- Dynamic UI & ARIA
- Testing & Continuous Integration
- Performance & Accessibility
- Governance & Culture
- Next Steps
Why Accessibility Matters
- 1 in 6 people worldwide live with some form of disability (visual, auditory, motor, cognitive).
- Accessible sites load faster and rank higher, because semantic HTML improves SEO and Core Web Vitals.
- Legal risk is rising: the EU EAA can fine up to 4 % of global turnover; U.S. ADA lawsuits increase every year.
ROI: A Microsoft study showed that every 1 % accessibility improvement reduced support tickets by 4 %.
Regulations & Standards
Region | Primary Law | Standard | Deadline |
---|---|---|---|
EU | EAA (2019/882) | WCAG 2.2 AA | 28 Jun 2025 |
US | ADA Title III + §508 | WCAG 2.2 AA | Ongoing |
Canada | AODA | WCAG 2.2 AA | 1 Jan 2026 |
WCAG 2.2 Highlights
New AA criteria:
ID | Name | Key Point |
---|---|---|
2.2.4 | Animation from Interactions | Users must be able to disable motion. |
2.2.5 | Target Size (Minimum) | Touch/click targets ≥ 44 × 44 px. |
2.4.11 | Focus Appearance | Visible focus outline ≥ 3 : 1 contrast ratio. |
Semantic Foundations
Using semantic HTML is the fastest win: native elements carry built-in keyboard, ARIA roles, and screen-reader support.
<!-- ✗ Anti-pattern -->
<div class="btn" onclick="save()">Save</div>
<!-- ✓ Semantic -->
<button type="button" class="btn-primary" onclick="save()">Save</button>
Landmark Elements
Tag | ARIA Role | Purpose |
---|---|---|
<header> |
banner |
Site or section header |
<nav> |
navigation |
Primary navigation links |
<main> |
main |
Unique page content |
<aside> |
complementary |
Sidebar / related links |
<footer> |
contentinfo |
Copyright & legal |
Deep dive → Semantic HTML for Accessibility
Keyboard Navigation & Focus
If a user can’t complete a task with only the keyboard, you fail WCAG A.
- Logical order follows DOM source.
- Visible focus — never hide outlines.
- Provide a Skip Link:
<!-- Adds a visible skip link only when focused -->
<a class="sr-only focus:not-sr-only" href="#main">
Skip to main content
</a>
Deep dive → Keyboard Navigation Best Practices
Visual Design & Color Contrast
Context | Minimum Ratio |
---|---|
Normal text | 4.5 : 1 |
Large/bold text (≥ 24 px or 18 px bold) | 3 : 1 |
Non-text UI (icons, borders) | 3 : 1 |
Use Figma plugins or Tailwind contrast utilities to enforce ratios.
Rich Media & Motion
Media | Requirement | WCAG ref |
---|---|---|
Images | Descriptive alt text |
1.1.1 |
Video | Captions & transcript | 1.2.2 / 1.2.3 |
Audio | Transcript | 1.2.1 |
Motion | Respect prefers-reduced-motion |
2.2.4 |
/* Disable animations for users who prefer reduced motion */
@media (prefers-reduced-motion: reduce) {
.spin { animation: none; }
.fade-in { animation: none; }
.parallax { transform: translateZ(0); }
}
Forms & Error Handling
- Every input has a
<label>
withfor
+id
. - Inline errors announced via ARIA-live:
<form id="signup" novalidate>
<label for="email">Email</label>
<input id="email" name="email" type="email" required />
<div id="email-error"
role="alert"
aria-live="assertive"
class="text-red-600 mt-2">
<!-- Error message injected here -->
</div>
<button type="submit">Sign up</button>
</form>
- Validate on submit and blur, not on every key-press.
Dynamic UI & ARIA
“No ARIA is better than bad ARIA.” — WAI
Accessible Tabs (minimal pattern)
<!-- Tabs -->
<div role="tablist" aria-label="Billing options">
<button id="basic-tab"
role="tab"
aria-selected="true"
aria-controls="basic-panel">
Basic
</button>
<button id="pro-tab"
role="tab"
aria-selected="false"
aria-controls="pro-panel">
Pro
</button>
</div>
<!-- Panels -->
<section id="basic-panel"
role="tabpanel"
aria-labelledby="basic-tab">
…Basic plan details…
</section>
<section id="pro-panel"
role="tabpanel"
aria-labelledby="pro-tab"
hidden>
…Pro plan details…
</section>
Deep dive → ARIA Roles Explained
Testing & Continuous Integration
Layer | Tools | Coverage |
---|---|---|
Static lint | ESLint jsx-a11y , Stylelint |
~20 % |
Automated UI | Axe-core, Pa11y, Lighthouse-CI | 40–50 % |
Manual | NVDA, VoiceOver, keyboard | Remaining |
GitHub Actions sample
name: accessibility-check
on: [pull_request]
jobs:
axe-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Run A11 against local build
run: |
npx @a11ysprint/cli scan http://localhost:3000 \
--output ci-report.html \
--fail-on critical
Block merges on critical issues; auto-open tickets for major.
Performance & Accessibility
Fast pages help users with cognitive and motor impairments.
Metric | Target | Quick Fix |
---|---|---|
LCP | < 2.5 s | Lazy-load images, preconnect fonts |
CLS | < 0.1 | Reserve space, avoid FOUT |
TBT | < 200 ms | Split bundles, web-workers |
Governance & Culture
- Assign an Accessibility Champion per squad.
- Set an OKR: “≤ 5 critical issues per release.”
- Conduct quarterly manual audits and team training.
Next Steps
- Run a free WCAG 2.2 scan → Start now
- Dive into the cluster articles linked above.
- Integrate our CLI into CI.
- Share this guide with your team and book a 5-Day Accessibility Sprint if you’re racing the EAA deadline.
Additional Resources
© 2025 Accessibility Sprint AB — Reproduction permitted with attribution.