FreeTool
Developer Tools

Web Accessibility (a11y): A Practical Guide for Developers

Learn the essential accessibility principles, ARIA attributes, keyboard navigation patterns, and testing tools that make your web apps usable by everyone.

8 min read

Person using a laptop with assistive technology

Over 1.3 billion people — 16% of the world's population — live with some form of disability. Web accessibility ensures your site works for users who rely on screen readers, keyboard navigation, switch access, or other assistive technologies. Beyond inclusion, accessible sites rank better in search, perform better on mobile, and are often legally required.

The four POUR principles

The Web Content Accessibility Guidelines (WCAG) are built on four core principles. Content must be:

  1. Perceivable — Information must be presentable in ways users can perceive (not just visual).
  2. Operable — All functionality must be available from a keyboard.
  3. Understandable — Content and interfaces must be understandable.
  4. Robust — Content must be parseable by assistive technologies.

WCAG 2.2 defines three compliance levels:

  • A — Minimum (must-have)
  • AA — Standard target for most organizations
  • AAA — Maximum (aspirational for some content)

Most legal requirements (ADA, EN 301 549, EAA) require AA conformance.

Semantic HTML first

The most impactful thing you can do for accessibility is use the right HTML element for the job. Browsers and screen readers already know what to do with semantic elements:

<!-- ❌ Div soup — no semantics -->
<div class="header">
  <div class="nav">
    <div class="nav-item" onclick="navigate()">Home</div>
  </div>
</div>

<!-- ✅ Semantic HTML — screen readers understand this -->
<header>
  <nav>
    <a href="/">Home</a>
  </nav>
</header>

Key semantic elements and their roles:

Element Role
<header>, <footer> Landmark regions
<nav> Navigation landmark
<main> Main content (one per page)
<aside> Complementary content
<h1><h6> Heading hierarchy
<button> Interactive control
<a href> Navigation link
<label> Form field label
<table> Tabular data

Images and alt text

Every meaningful image needs an alt attribute describing its content. Decorative images get an empty alt="" so screen readers skip them:

<!-- Meaningful image -->
<img src="chart.png" alt="Bar chart showing 40% increase in revenue Q1 2026">

<!-- Decorative image -->
<img src="divider.svg" alt="">

<!-- Icon button — describe the action, not the icon -->
<button>
  <img src="trash.svg" alt="Delete item">
</button>

<!-- Avoid: redundant "image of" -->
<!-- ❌ --> <img src="cat.jpg" alt="Image of a cat">
<!-- ✅ --> <img src="cat.jpg" alt="Orange tabby cat sitting on a windowsill">

Color contrast

Users with low vision or color blindness rely on sufficient contrast between text and background.

WCAG AA requirements:

  • Normal text (< 18pt): 4.5:1 contrast ratio minimum
  • Large text (≥ 18pt or 14pt bold): 3:1 contrast ratio minimum
  • UI components and graphical objects: 3:1 minimum

Don't rely on color alone to convey information:

<!-- ❌ Color-only status indicator -->
<span class="text-red-500">Error</span>

<!-- ✅ Color + icon + text -->
<span class="text-red-500 flex items-center gap-1">
  <svg aria-hidden="true"><!-- error icon --></svg>
  Error: Invalid email address
</span>

Forms: labels, errors, and descriptions

Every form control needs a visible, associated label:

<!-- ✅ Explicit label association -->
<label for="email">Email address</label>
<input id="email" type="email" aria-describedby="email-hint email-error">
<p id="email-hint" class="text-sm text-gray-500">We'll never share your email.</p>
<p id="email-error" role="alert" class="text-sm text-red-600" hidden>
  Please enter a valid email address.
</p>

Key form accessibility patterns:

  • Use for/id to associate labels with inputs
  • Use aria-describedby for hint text and error messages
  • Use role="alert" or aria-live="polite" for dynamic error messages
  • Use aria-required="true" or the native required attribute
  • Group related inputs with <fieldset> and <legend>

Keyboard navigation

All interactive elements must be reachable and operable with a keyboard:

  • Tab — move forward through focusable elements
  • Shift+Tab — move backward
  • Enter/Space — activate buttons, checkboxes
  • Arrow keys — navigate within components (menus, tabs, sliders)
  • Escape — close modals, dismiss dropdowns

Focus indicators must be visible. Never do this without an alternative:

/* ❌ Hides focus indicator entirely */
*:focus { outline: none; }

/* ✅ Custom focus style that's still visible */
*:focus-visible {
  outline: 2px solid #3b82f6;
  outline-offset: 2px;
}

ARIA: when and how to use it

ARIA (Accessible Rich Internet Applications) attributes add semantic meaning when HTML alone isn't sufficient. The first rule of ARIA: don't use ARIA if native HTML can do the job.

<!-- Labeling elements without visible labels -->
<button aria-label="Close dialog">✕</button>

<!-- Describing expanded state -->
<button aria-expanded="false" aria-controls="menu">Menu</button>
<ul id="menu" hidden>...</ul>

<!-- Live region for dynamic content -->
<div aria-live="polite" aria-atomic="true">
  <!-- Screen readers announce changes to this area -->
  3 results found
</div>

<!-- Landmark role when semantic element isn't available -->
<div role="search">
  <input type="search" placeholder="Search...">
</div>

Focus management for SPAs and modals

In single-page applications, page navigation doesn't trigger a browser focus reset. When a "page" loads, move focus to a meaningful location:

// After navigation, focus the main heading
document.querySelector("h1")?.focus();

For modals:

  1. When the modal opens, move focus to the first focusable element inside it
  2. Trap focus within the modal while it's open (prevent Tab from reaching content behind)
  3. When the modal closes, return focus to the element that triggered it

Testing for accessibility

Automated tools (catch ~30-40% of issues)

  • axe DevTools browser extension
  • Lighthouse accessibility audit in Chrome DevTools
  • WAVE browser extension

Manual testing (required for full coverage)

  1. Keyboard-only navigation — unplug your mouse and navigate your entire site
  2. Screen reader testing — NVDA + Firefox (Windows), VoiceOver + Safari (Mac/iOS)
  3. Zoom to 200% — ensure no content is lost or overlaps
  4. Color blind simulation — browser DevTools → Rendering → Emulate vision deficiencies

Readability

Plain language improves accessibility for users with cognitive disabilities. Use our Readability Score tool to check the reading level of your content — aim for Grade 8-10 for general audiences.

Quick wins you can do today

  1. Add alt text to all images
  2. Make sure all form inputs have associated <label> elements
  3. Check color contrast meets 4.5:1 for body text
  4. Add :focus-visible styles and never remove outlines without a replacement
  5. Use <button> for actions and <a href> for navigation (never the reverse)
  6. Add lang="en" (or appropriate language) to your <html> element
  7. Use one <h1> per page and maintain a logical heading hierarchy

Accessibility isn't an add-on — it's a quality characteristic. Building accessible products from the start is far cheaper than retrofitting later, and it makes your products better for everyone.