Web Accessibility

Guidelines

WCAG 2.1 · Building for Everyone

What is Web Accessibility?

Web accessibility means designing and building websites and applications that everyone can use — including people with disabilities affecting:

  • Vision — blindness, low vision, color blindness
  • Hearing — deafness, hard of hearing
  • Motor — limited fine motor control, no keyboard/mouse use
  • Cognitive — dyslexia, ADHD, memory impairments
  • Speech — for voice-input users

"The power of the Web is in its universality. Access by everyone regardless of disability is an essential aspect."
— Tim Berners-Lee

The WCAG Framework

Web Content Accessibility Guidelines (WCAG) are organized around 4 core principles — the POUR model:

PrincipleMeaning
PerceivableInfo must be presentable to users in ways they can perceive
OperableUI components and navigation must be operable
UnderstandableContent and operation must be understandable
RobustContent must be robust enough for assistive tech

Each principle contains guidelines with testable success criteria rated at three levels: A, AA, and AAA.

Conformance Levels

A Minimum — addresses the most basic requirements. Without this, some users simply cannot access content at all.

AA Standard — the widely accepted target. Required by most legal standards (ADA, Section 508, EN 301 549).

AAA Enhanced — highest level. Not always feasible for all content, but ideal where achievable.

Tip for teams: Target AA as your baseline for production. Prioritize A criteria first during audits — they're blockers, not improvements.

1 · Perceivable — Text Alternatives

Every non-text content must have a text alternative that serves the equivalent purpose.

<!-- ✅ Good: Descriptive alt text -->
<img
  src="checkout-flow.png"
  alt="Three-step checkout: cart, shipping, payment"
/>

<!-- ✅ Good: Decorative image, hidden from screen readers -->
<img src="divider.svg" alt="" role="presentation" />

<!-- ❌ Bad: No alt, assistive tech reads the filename -->
<img src="hero_img_final_v3.png" />

Icons with meaning also need labels:

<button aria-label="Close dialog">
  <svg aria-hidden="true">...</svg>
</button>

1 · Perceivable — Color & Contrast

Never use color as the only means of conveying information.

/* ❌ Bad: Red/green only — invisible to color-blind users */
.error {
  color: red;
}
.success {
  color: green;
}

/* ✅ Good: Color + icon/label + pattern */
.error {
  color: #dc2626;
  border-left: 3px solid #dc2626;
}
.success {
  color: #16a34a;
  border-left: 3px solid #16a34a;
}

Minimum Contrast Ratios (WCAG AA)

Content TypeRatio
Normal text (< 18pt)4.5 : 1
Large text (≥ 18pt or 14pt bold)3 : 1
UI components & graphics3 : 1

1 · Perceivable — Captions & Media

For audio and video content:

  • Prerecorded video → synchronized captions (AA)
  • Prerecorded audio-only → text transcript (A)
  • Live audio/video → real-time captions (AA)
  • Prerecorded video → audio description for visual-only content (AA)
<video controls>
  <source src="demo.mp4" type="video/mp4" />
  <track
    kind="captions"
    src="demo-captions.vtt"
    srclang="en"
    label="English"
    default
  />
</video>

2 · Operable — Keyboard Navigation

All functionality must be accessible via keyboard alone. No mouse required.

<!-- ✅ Good: Native elements are keyboard-accessible by default -->
<button onclick="openModal()">Open Settings</button>

<!-- ❌ Bad: div with click — not focusable, not keyboard operable -->
<div onclick="openModal()">Open Settings</div>

<!-- ✅ Fix: Add role + tabindex + keydown handler -->
<div
  role="button"
  tabindex="0"
  onclick="openModal()"
  onkeydown="if(e.key==='Enter'||e.key===' ')openModal()"
>
  Open Settings
</div>

Golden rule: Prefer native HTML elements. They come with keyboard behavior for free.

2 · Operable — Focus Management

Users must always know where focus is on the page.

/* ❌ Never do this globally */
* {
  outline: none;
}

/* ✅ Style focus visibly — don't remove it */
:focus-visible {
  outline: 2px solid #38bdf8;
  outline-offset: 3px;
  border-radius: 3px;
}

Focus order must follow a logical, meaningful sequence — typically matching DOM order.

When opening a modal or dialog, move focus into it. When closing, return focus to the trigger element.

// After opening dialog
dialogEl.querySelector("[data-autofocus]").focus();

2 · Operable — Skip Navigation

Users navigating by keyboard shouldn't tab through every nav link on every page.

<!-- Place this as the first element in <body> -->
<a href="#main-content" class="skip-link"> Skip to main content </a>

<nav><!-- ... many links ... --></nav>

<main id="main-content" tabindex="-1">
  <!-- Page content starts here -->
</main>
.skip-link {
  position: absolute;
  top: -100%;
  left: 1rem;
  padding: 0.5rem 1rem;
  background: #0a0f1e;
  color: #38bdf8;
  z-index: 9999;
}
.skip-link:focus {
  top: 1rem;
}

2 · Operable — Seizures & Motion

Flashing content can trigger seizures. The rule: nothing flashes more than 3 times per second.

For animation and motion:

/* ✅ Respect the user's motion preference */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

This is an easy win. Add it to every project's global stylesheet.

3 · Understandable — Language & Labels

Declare the page language so screen readers use the correct pronunciation engine:

<html lang="en"></html>

For inline content in another language:

<p>The French word <span lang="fr">bonjour</span> means "hello."</p>

Form Labels

Every input must have a visible, associated label:

<!-- ✅ Explicit label association -->
<label for="email">Email address</label>
<input type="email" id="email" name="email" autocomplete="email" />

<!-- ❌ Placeholder is NOT a label substitute -->
<input type="email" placeholder="Email address" />

3 · Understandable — Error Handling

Error messages must be specific, visible, and programmatically associated with their fields.

<div>
  <label for="dob">Date of birth</label>
  <input
    type="text"
    id="dob"
    aria-describedby="dob-error"
    aria-invalid="true"
  />
  <p id="dob-error" role="alert">
    ⚠ Enter a date in MM/DD/YYYY format, e.g. 04/15/1990
  </p>
</div>

Best practices:

  • Identify the field with the error
  • Describe what went wrong
  • Suggest how to fix it
  • Don't rely on color alone to indicate errors

4 · Robust — Semantic HTML

Use HTML elements for their intended purpose. Semantics give assistive technologies meaning.

<!-- ❌ Div soup — meaningless to screen readers -->
<div class="header">
  <div class="nav">
    <div class="nav-link">Home</div>
  </div>
</div>

<!-- ✅ Semantic landmarks — screen readers can navigate by these -->
<header>
  <nav aria-label="Main navigation">
    <a href="/">Home</a>
  </nav>
</header>
<main>...</main>
<footer>...</footer>

Landmarks: header, nav, main, aside, footer, section, article

4 · Robust — ARIA

ARIA (Accessible Rich Internet Applications) fills semantic gaps for dynamic UI.

First rule of ARIA: Don't use ARIA if native HTML can do the job.

<!-- Live region: announces dynamic updates to screen readers -->
<div aria-live="polite" aria-atomic="true" id="status-msg">
  Form submitted successfully.
</div>

<!-- Expandable section -->
<button aria-expanded="false" aria-controls="section1">Toggle Section</button>
<div id="section1" hidden>...</div>

<!-- Progress indicator -->
<div
  role="progressbar"
  aria-valuenow="65"
  aria-valuemin="0"
  aria-valuemax="100"
>
  65%
</div>

Testing for Accessibility

A layered approach catches the most issues:

MethodWhat it catchesTools
Automated scan~30–40% of issuesaxe DevTools, Lighthouse, WAVE
Keyboard testingFocus, operabilityJust your keyboard
Screen reader testingName, role, valueNVDA, JAWS, VoiceOver
Manual auditContext, logic, UXWCAG checklist
User testingReal-world gapsDisabled users

Automate what you can, but automated tools alone are not enough.

Common Accessibility Mistakes

Quick checklist of the most frequent failures:

  • ❌ Images missing alt text
  • ❌ Form inputs without visible labels
  • ❌ Insufficient color contrast
  • ❌ outline: none on focusable elements
  • ❌ Interactive elements that aren't keyboard operable
  • ❌ Missing lang attribute on <html>
  • ❌ Modal dialogs that don't trap or manage focus
  • ❌ Error messages not associated with inputs
  • ❌ Auto-playing media with no pause control
  • ❌ Relying on color alone to convey status

Accessibility is increasingly a legal requirement, not just best practice:

RegionStandard
🇺🇸 United StatesADA (Title III) · Section 508
🇪🇺 European UnionEN 301 549 · European Accessibility Act
🇬🇧 United KingdomEquality Act 2010
🇨🇦 CanadaAODA · ACA
🇦🇺 AustraliaDDA

Most reference WCAG 2.1 AA as the technical standard. Lawsuits targeting inaccessible web experiences have grown significantly year over year across the US and EU.

Key Takeaways

Accessibility is not a feature — it's a baseline.

  1. Follow the POUR principles: Perceivable, Operable, Understandable, Robust
  2. Target WCAG 2.1 AA as your production standard
  3. Use semantic HTML first — ARIA only when needed
  4. Ensure keyboard operability and visible focus for all interactive elements
  5. Meet contrast ratios and never use color as the sole signal
  6. Provide text alternatives for all non-text content
  7. Test with automated tools + keyboard + screen reader
  8. Bake accessibility in from the start — retrofitting is expensive

Build for Everyone.

Resources

  • W3C WCAG 2.1 — w3.org/TR/WCAG21
  • WebAIM — webaim.org
  • A11y Project — a11yproject.com
  • MDN Accessibility — developer.mozilla.org/accessibility
  • axe DevTools — deque.com/axe

Accessibility benefits all users — not just those with disabilities.