Web Development Intermediate

Complete Guide to Web Accessibility

Master WCAG guidelines, ARIA, and inclusive design principles

18 min read โ€ข Updated January 2025

What is Web Accessibility and Why It Matters

Web accessibility (often abbreviated as a11y) means designing and developing websites and applications that can be used by everyone, including people with disabilities. This includes people with visual, auditory, motor, cognitive, and neurological disabilities.

According to the World Health Organization, over 1 billion people globally live with some form of disability. By making your website accessible, you're not just following best practicesโ€”you're ensuring that your content and services are available to everyone, regardless of their abilities.

Why Accessibility Matters:

  • Legal Compliance: Many countries require websites to be accessible (ADA, Section 508, European Accessibility Act)
  • Larger Audience: 15% of the world's population has some form of disability
  • Better UX for Everyone: Accessible design often improves usability for all users
  • SEO Benefits: Many accessibility practices align with search engine optimization
  • Brand Reputation: Demonstrates commitment to inclusivity and social responsibility
  • Economic Impact: People with disabilities represent $13 trillion in annual disposable income globally

WCAG 2.1 Guidelines (A, AA, AAA)

The Web Content Accessibility Guidelines (WCAG) 2.1, published by the W3C, is the internationally recognized standard for web accessibility. It's organized around four principles known as POUR:

Perceivable - Information must be presentable to users in ways they can perceive

  • Provide text alternatives for non-text content
  • Provide captions and alternatives for multimedia
  • Create content that can be presented in different ways
  • Make it easier for users to see and hear content

Operable - User interface components must be operable

  • Make all functionality available from a keyboard
  • Give users enough time to read and use content
  • Do not design content that causes seizures
  • Help users navigate and find content

Understandable - Information and operation must be understandable

  • Make text readable and understandable
  • Make content appear and operate in predictable ways
  • Help users avoid and correct mistakes

Robust - Content must be robust enough to work with current and future technologies

  • Maximize compatibility with current and future user tools
  • Use valid, clean markup
  • Ensure proper parsing by assistive technologies

Conformance Levels

  • Level A (Minimum): Basic accessibility features that all websites should meet
  • Level AA (Mid Range): The recommended level for most websites; required by many laws
  • Level AAA (Highest): Enhanced accessibility for specialized audiences; not required for entire sites

Color Contrast Requirements

Color contrast is crucial for users with low vision, color blindness, or anyone viewing your site in bright sunlight. WCAG defines specific contrast ratios that must be met:

WCAG AA Requirements (Standard)

  • Normal text: 4.5:1 contrast ratio minimum
  • Large text (18pt+ or 14pt+ bold): 3:1 contrast ratio minimum
  • UI components and graphics: 3:1 contrast ratio against adjacent colors

WCAG AAA Requirements (Enhanced)

  • Normal text: 7:1 contrast ratio minimum
  • Large text: 4.5:1 contrast ratio minimum

Before and After Example:

โŒ Bad: This text has 1.6:1 contrast (fails WCAG)

Light coral on misty rose is virtually unreadable

โœ“ Good: This text has 12.6:1 contrast (exceeds WCAG AAA)

Dark gray on white is highly readable

Use Our Tools

Check your color combinations with our Color Contrast Checker or preview your site with our Color Blindness Simulator.

Pro Tips for Color Contrast:

  • Don't rely on color alone to convey information (use icons, text, or patterns)
  • Test your design in grayscale to ensure contrast is sufficient
  • Avoid pure red/green combinations (problematic for colorblind users)
  • Provide high contrast mode as an option
  • Consider using dark mode with proper contrast ratios

Semantic HTML and ARIA

Semantic HTML uses meaningful elements that describe their content, making it easier for assistive technologies to understand and navigate your page. ARIA (Accessible Rich Internet Applications) supplements HTML with additional attributes when semantic HTML alone isn't sufficient.

Semantic HTML Examples:

โŒ Bad: Using divs for everything

<div class="header">
  <div class="nav">
    <div class="link">Home</div>
  </div>
</div>
<div class="main">
  <div class="article">Content</div>
</div>

โœ“ Good: Using semantic elements

<header>
  <nav>
    <a href="/">Home</a>
  </nav>
</header>
<main>
  <article>Content</article>
</main>

Essential Semantic Elements:

  • <header>, <nav>, <main>, <footer> - Page structure
  • <article>, <section>, <aside> - Content grouping
  • <h1> through <h6> - Heading hierarchy (never skip levels)
  • <button> - Interactive buttons (not <div> with click handlers)
  • <a> - Links to navigate (not <span> with click handlers)

ARIA Attributes:

Use ARIA when semantic HTML isn't sufficient. Follow the first rule of ARIA: "Don't use ARIA if you can use native HTML."

ARIA Roles:

<!-- Custom tab interface -->
<div role="tablist">
  <button role="tab" aria-selected="true">Tab 1</button>
  <button role="tab" aria-selected="false">Tab 2</button>
</div>
<div role="tabpanel">Tab 1 content</div>

ARIA States and Properties:

<!-- Expandable section -->
<button
  aria-expanded="false"
  aria-controls="content"
>
  Show more
</button>
<div id="content" hidden>Content here</div>

<!-- Form with error -->
<input
  type="email"
  aria-invalid="true"
  aria-describedby="email-error"
/>
<span id="email-error">Please enter a valid email</span>

ARIA Labels:

<!-- Icon button -->
<button aria-label="Close dialog">
  <svg><!-- X icon --></svg>
</button>

<!-- Search landmark -->
<form role="search" aria-label="Site search">
  <input type="search" />
</form>

Common ARIA Attributes:

  • aria-label - Provides a label when none is visible
  • aria-labelledby - References an element that labels the current element
  • aria-describedby - References elements that describe the current element
  • aria-hidden="true" - Hides decorative elements from screen readers
  • aria-live - Announces dynamic content changes (polite, assertive, off)
  • aria-expanded - Indicates if a collapsible element is expanded
  • aria-current - Indicates the current item in a set (page, step, location)

Keyboard Navigation

Many users rely on keyboards instead of mice due to motor disabilities, preference, or efficiency. Your website must be fully navigable using only a keyboard.

Essential Keyboard Keys:

  • Tab - Move focus forward through interactive elements
  • Shift + Tab - Move focus backward
  • Enter - Activate buttons and links
  • Space - Activate buttons, toggle checkboxes
  • Arrow keys - Navigate within components (menus, tabs, radio groups)
  • Escape - Close dialogs, cancel actions
  • Home/End - Jump to start/end of lists or content

Making Custom Components Keyboard Accessible:

โŒ Bad: Div with click handler (not keyboard accessible)

<div class="button" onclick="doSomething()">
  Click me
</div>

โœ“ Good: Semantic button (keyboard accessible by default)

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

โš  Acceptable: Custom element with proper ARIA and keyboard handling

<div
  role="button"
  tabindex="0"
  onclick="doSomething()"
  onkeydown="handleKeyDown(event)"
>
  Click me
</div>

<script>
function handleKeyDown(event) {
  if (event.key === 'Enter' || event.key === ' ') {
    event.preventDefault();
    doSomething();
  }
}
</script>

Tab Order and tabindex:

  • tabindex="0" - Makes element focusable in natural tab order
  • tabindex="-1" - Makes element programmatically focusable (not in tab order)
  • tabindex="1+" - Avoid! Creates confusing tab order
  • Interactive elements (buttons, links, inputs) are focusable by default
  • Maintain logical, predictable tab order (top to bottom, left to right)

Skip Links:

Provide a "Skip to main content" link as the first focusable element to let keyboard users bypass repetitive navigation.

<a href="#main" class="skip-link">
  Skip to main content
</a>

<style>
.skip-link {
  position: absolute;
  left: -9999px;
  z-index: 999;
}

.skip-link:focus {
  left: 10px;
  top: 10px;
}
</style>

Screen Reader Compatibility

Screen readers are software applications that convert digital text into synthesized speech or braille. They're essential for blind and visually impaired users. Popular screen readers include JAWS, NVDA, VoiceOver, and TalkBack.

Alternative Text for Images:

โŒ Bad: Missing or poor alt text

<img src="photo.jpg" alt="image">
<img src="logo.png" alt="">
<img src="chart.png">

โœ“ Good: Descriptive alt text

<img src="logo.png" alt="Company Name">
<img src="chart.png" alt="Bar chart showing 25% increase in sales from Q1 to Q2">

<!-- Decorative image -->
<img src="decorative.png" alt="" role="presentation">

Form Labels and Instructions:

โŒ Bad: Placeholder as label

<input type="email" placeholder="Email address">

โœ“ Good: Proper label association

<label for="email">Email address</label>
<input type="email" id="email" required>

<!-- Or with aria-label -->
<input
  type="email"
  aria-label="Email address"
  placeholder="you@example.com"
>

Announcing Dynamic Content:

Use ARIA live regions to announce content that changes without page reload.

<!-- Status message -->
<div role="status" aria-live="polite">
  <!-- Content updated via JavaScript -->
  Item added to cart
</div>

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

<!-- Loading state -->
<div aria-live="polite" aria-busy="true">
  Loading...
</div>

Landmark Regions:

Use landmarks to help screen reader users navigate page sections quickly.

<header><!-- banner landmark --></header>
<nav aria-label="Main navigation"><!-- navigation landmark --></nav>
<main><!-- main landmark --></main>
<aside><!-- complementary landmark --></aside>
<footer><!-- contentinfo landmark --></footer>

<!-- Search landmark -->
<form role="search" aria-label="Site search">
  <input type="search" />
</form>

Screen Reader Best Practices:

  • Use proper heading hierarchy (h1 โ†’ h2 โ†’ h3, never skip levels)
  • Provide descriptive link text ("Read more about accessibility" not "Click here")
  • Hide decorative images with alt="" or aria-hidden="true"
  • Ensure error messages are associated with form fields
  • Provide text alternatives for audio and video content
  • Test with actual screen readers (NVDA is free on Windows)

Focus Management and Visual Indicators

Focus indicators show keyboard users which element currently has focus. They're essential for keyboard navigation and must be clearly visible.

Focus Indicator Best Practices:

โŒ Bad: Removing focus outline

/* NEVER do this without providing alternative */
*:focus {
  outline: none;
}

โœ“ Good: Custom focus styles with sufficient contrast

:focus {
  outline: 2px solid #005fcc;
  outline-offset: 2px;
}

/* Or use modern focus-visible */
:focus-visible {
  outline: 3px solid #005fcc;
  outline-offset: 3px;
}

/* Remove outline only for mouse users */
:focus:not(:focus-visible) {
  outline: none;
}

Managing Focus in Dynamic Content:

When opening modals or navigating SPAs, move focus appropriately.

// Opening a modal
function openModal() {
  const modal = document.getElementById('modal');
  const firstFocusable = modal.querySelector('button, a, input');

  modal.style.display = 'block';
  firstFocusable.focus();

  // Trap focus inside modal
  modal.addEventListener('keydown', trapFocus);
}

// Closing a modal
function closeModal(triggerElement) {
  const modal = document.getElementById('modal');
  modal.style.display = 'none';

  // Return focus to element that opened modal
  triggerElement.focus();
}

Focus Trapping in Modals:

Prevent focus from leaving modal dialogs until closed.

function trapFocus(event) {
  const modal = event.currentTarget;
  const focusableElements = modal.querySelectorAll(
    'button, a, input, select, textarea, [tabindex]:not([tabindex="-1"])'
  );
  const firstElement = focusableElements[0];
  const lastElement = focusableElements[focusableElements.length - 1];

  if (event.key === 'Tab') {
    if (event.shiftKey && document.activeElement === firstElement) {
      event.preventDefault();
      lastElement.focus();
    } else if (!event.shiftKey && document.activeElement === lastElement) {
      event.preventDefault();
      firstElement.focus();
    }
  }

  if (event.key === 'Escape') {
    closeModal();
  }
}

Focus Management Tips:

  • Focus indicators must have at least 3:1 contrast ratio with background
  • Use :focus-visible to show focus only for keyboard users
  • Move focus to new content after navigation or dynamic updates
  • Never remove focus indicators without providing visible alternatives
  • Consider using tabindex="-1" on headings to allow programmatic focus
  • Test tab order matches visual layout

Testing for Accessibility

Testing is essential to ensure your site is actually accessible. Use a combination of automated tools and manual testing for comprehensive coverage.

Automated Testing Tools:

  • axe DevTools: Browser extension for Chrome/Firefox, finds 57% of WCAG issues
  • Lighthouse: Built into Chrome DevTools, provides accessibility audit
  • WAVE: Web Accessibility Evaluation Tool by WebAIM
  • Pa11y: Command-line tool for automated testing
  • axe-core: JavaScript library for automated testing in your CI/CD pipeline
  • Deque University: Comprehensive testing platform

Note: Automated tools only catch ~30-50% of accessibility issues. Manual testing is essential.

Manual Testing Checklist:

  • Keyboard only: Navigate entire site using only keyboard (no mouse)
  • Screen reader: Test with NVDA (Windows), VoiceOver (Mac), or JAWS
  • Zoom to 200%: Ensure content remains usable and doesn't break
  • Browser zoom: Test with text-only zoom at 200%
  • Color contrast: Check all text/background combinations meet ratios
  • Color blindness: View site with color blindness simulators
  • Forms: Complete all forms using keyboard and screen reader
  • Videos: Verify captions are accurate and synchronized

Screen Reader Testing Commands:

NVDA (Windows - Free):

  • Ctrl + Alt + N - Start NVDA
  • H - Jump to next heading
  • D - Jump to next landmark
  • Insert + F7 - List of elements

VoiceOver (Mac):

  • Cmd + F5 - Start VoiceOver
  • VO + Right Arrow - Next item
  • VO + U - Rotor (element list)

Testing with Real Users:

The best way to ensure accessibility is to test with people who have disabilities:

  • Recruit users with various disabilities for usability testing
  • Use services like UserTesting.com or AccessibilityOz for expert audits
  • Join disability advocacy groups and request feedback
  • Hire accessibility consultants for comprehensive audits
  • Consider continuous accessibility monitoring services

Continuous Testing

Integrate accessibility testing into your CI/CD pipeline with tools like axe-core, Pa11y CI, or Cypress with axe. This catches regressions before they reach production.

Common Accessibility Mistakes

Even experienced developers make these accessibility mistakes. Learn to recognize and avoid them:

โŒ Mistake 1: Missing Alt Text

Images without alt text are invisible to screen readers.

<!-- Bad -->
<img src="product.jpg">

<!-- Good -->
<img src="product.jpg" alt="Blue running shoes with white laces">

โŒ Mistake 2: Poor Color Contrast

Light gray text on white backgrounds is difficult to read.

/* Bad - 2:1 contrast */
.text { color: #999; background: #fff; }

/* Good - 4.5:1 contrast */
.text { color: #595959; background: #fff; }

โŒ Mistake 3: Unlabeled Form Inputs

Inputs without labels confuse screen reader users.

<!-- Bad -->
<input type="text" placeholder="Name">

<!-- Good -->
<label for="name">Name</label>
<input type="text" id="name" placeholder="John Doe">

โŒ Mistake 4: Non-Semantic Buttons

Divs with click handlers aren't keyboard accessible.

<!-- Bad -->
<div onclick="submit()">Submit</div>

<!-- Good -->
<button onclick="submit()">Submit</button>

โŒ Mistake 5: Skipping Heading Levels

Jumping from h1 to h3 breaks document structure.

<!-- Bad -->
<h1>Page Title</h1>
<h3>Section</h3>

<!-- Good -->
<h1>Page Title</h1>
<h2>Section</h2>
<h3>Subsection</h3>

โŒ Mistake 6: "Click Here" Links

Non-descriptive link text provides no context.

<!-- Bad -->
<a href="/guide">Click here</a> to read more.

<!-- Good -->
<a href="/guide">Read our accessibility guide</a>

โŒ Mistake 7: Auto-Playing Media

Videos that auto-play with sound are disorienting.

<!-- Bad -->
<video src="video.mp4" autoplay></video>

<!-- Good -->
<video src="video.mp4" controls>
  <track kind="captions" src="captions.vtt">
</video>

โŒ Mistake 8: Removing Focus Outlines

Removing outlines without alternatives makes keyboard navigation impossible.

/* Bad */
*:focus { outline: none; }

/* Good */
:focus-visible {
  outline: 2px solid #005fcc;
  outline-offset: 2px;
}

Ready to Make Your Site Accessible?

Use our tools to test and improve your website's accessibility