Accessibility (a11y)

ARIA, screen readers, and inclusive design

What is Web Accessibility?

Web accessibility (a11yβ€”"a" + 11 letters + "y") ensures people with disabilities can use your website. This includes:

πŸ‘οΈ Visual

Blind, low vision, color blindness

πŸ‘‚ Auditory

Deaf or hard of hearing

πŸ–±οΈ Motor

Limited mobility, keyboard-only users

🧠 Cognitive

Learning disabilities, memory issues

πŸ’‘ Accessibility benefits everyoneβ€”mobile users, elderly, people with temporary injuries, users in bright sunlight, etc.

Semantic HTML: The Foundation

Using the right HTML elements provides built-in accessibility:

❌ Inaccessible

<div onclick="submit()">
  Click me
</div>

β€’ Not keyboard accessible (no Tab focus)

β€’ Screen readers don't know it's clickable

β€’ No Enter key support

βœ“ Accessible

<button onclick="submit()">
  Click me
</button>

βœ“ Keyboard accessible (Tab + Enter)

βœ“ Screen readers announce "Button: Click me"

βœ“ Works with assistive tech

Alt Text for Images

Essential for screen reader users and when images fail to load.

❌ Bad

<img src="dog.jpg" />

<img src="chart.png" alt="image" />

<img src="logo.svg" alt="logo.svg" />

Screen reader says nothing or unhelpful info

βœ“ Good

<img src="dog.jpg" alt="Golden retriever playing fetch" />

<img src="chart.png" alt="Sales increased 45% in Q4 2024" />

<!-- Decorative images -->
<img src="divider.png" alt="" />

Descriptive, contextual, or empty for decorative

Keyboard Navigation

Many users navigate without a mouse. Ensure all interactive elements are keyboard accessible.

Key Keyboard Controls

Tab: Navigate forward through interactive elements
Shift + Tab: Navigate backward
Enter: Activate buttons, links
Space: Activate buttons, toggle checkboxes
Arrow keys: Navigate radio buttons, dropdowns
Esc: Close modals, cancel actions

❌ Not keyboard accessible

<div onclick="showMenu()">
  Menu
</div>

<span onclick="openModal()">
  View details
</span>

βœ“ Keyboard accessible

<button onclick="showMenu()">
  Menu
</button>

<button onclick="openModal()">
  View details
</button>

ARIA (Accessible Rich Internet Applications)

ARIA attributes enhance accessibility when semantic HTML isn't enough.

⚠️ First Rule of ARIA: Don't use ARIA unless you have to!

Semantic HTML is usually better. Use ARIA only when HTML alone can't convey meaning.

aria-label

Provides accessible name when visible text isn't enough:

<button aria-label="Close dialog">
  Γ—
</button>

<button aria-label="Search">
  πŸ”
</button>

<a href="/profile" aria-label="View your profile">
  <img src="avatar.jpg" alt="" />
</a>

aria-labelledby & aria-describedby

Link elements to their labels/descriptions:

<h2 id="dialog-title">Confirm deletion</h2>
<p id="dialog-desc">This action cannot be undone.</p>
<div 
  role="dialog" 
  aria-labelledby="dialog-title"
  aria-describedby="dialog-desc"
>
  <!-- dialog content -->
</div>

aria-hidden

Hide decorative elements from screen readers:

<button>
  <span aria-hidden="true">β˜…</span>
  Favorite
</button>

<!-- Decorative icons -->
<span class="icon" aria-hidden="true">β†’</span>

role

Define element purpose when semantic HTML isn't available:

<div role="button" tabindex="0">
  Custom button
</div>

<div role="navigation">
  <!-- navigation links -->
</div>

<div role="alert">
  Form submitted successfully!
</div>

aria-live

Announce dynamic content changes:

<!-- Polite: wait for screen reader to finish -->
<div aria-live="polite">
  3 items in cart
</div>

<!-- Assertive: interrupt immediately -->
<div aria-live="assertive" role="alert">
  Error: Payment failed!
</div>

Focus Management

Make focus states visible and manage focus flow:

❌ Bad

/* Removes focus outline */
button:focus {
  outline: none;
}

Keyboard users can't see where they are

βœ“ Good

/* Custom focus styles */
button:focus {
  outline: 2px solid blue;
  outline-offset: 2px;
}

/* Or use :focus-visible */
button:focus-visible {
  outline: 2px solid blue;
}

Clear visual indicator for keyboard users

⚠️ Common Accessibility Mistakes

  • ❌ Using divs/spans for buttons: Use <button> instead
  • ❌ Missing alt text: Every image needs alt (even if empty for decorative)
  • ❌ Poor color contrast: Text must be readable (4.5:1 ratio minimum)
  • ❌ No keyboard access: All interactive elements must be keyboard accessible
  • ❌ Removing focus outlines: Don't use outline: none without custom focus styles
  • ❌ Auto-playing media: Don't auto-play videos/audio without controls
  • ❌ Form inputs without labels: Every input needs a <label>

βœ“ Accessibility Checklist

  • βœ“ Use semantic HTML: <button>, <nav>, <main>, etc.
  • βœ“ Provide alt text: Descriptive for meaningful images, empty for decorative
  • βœ“ Ensure keyboard navigation: Test with Tab key only
  • βœ“ Maintain color contrast: Use online contrast checkers
  • βœ“ Label all form inputs: Use <label for="id">
  • βœ“ Use ARIA sparingly: Only when semantic HTML isn't enough
  • βœ“ Provide skip links: "Skip to main content" for keyboard users
  • βœ“ Test with screen readers: NVDA (Windows), VoiceOver (Mac), JAWS
  • βœ“ Use heading hierarchy: h1 β†’ h2 β†’ h3 (don't skip levels)

πŸ› οΈ Testing Tools

  • Lighthouse: Built into Chrome DevTools, audits accessibility
  • WAVE: Browser extension for visual accessibility evaluation
  • axe DevTools: Comprehensive accessibility testing
  • Keyboard testing: Navigate your site using only Tab, Enter, Space, Arrow keys
  • Screen readers: NVDA (free, Windows), VoiceOver (Mac), JAWS (Windows)
  • Color contrast checker: WebAIM Contrast Checker