Web accessibility (a11y) is not just about compliance — accessible websites work better for everyone, including keyboard users, screen reader users, and people with temporary disabilities. The business case is strong: the EU Accessibility Act (2025) mandates accessibility for many digital products, and inaccessible websites lose an estimated 15-20% of potential users. This guide covers practical accessibility patterns that developers actually need.

Accessibility Basics: The 4 Principles (POUR)

PrincipleWhat It MeansDeveloper Checklist
PerceivableUsers can perceive the contentAlt text for images, captions for video, sufficient color contrast
OperableUsers can operate the interfaceKeyboard navigation, no keyboard traps, enough time to read
UnderstandableUsers can understand the contentReadable text, predictable navigation, input assistance (error messages)
RobustContent works with assistive technologiesSemantic HTML, valid ARIA (when needed), works across browsers

Semantic HTML: Your Best Accessibility Tool

The most important rule: Use semantic HTML elements. They are accessible by default — no ARIA needed.

Instead ofUseWhy
<div onclick="..."><button>Buttons are focusable, keyboard-activatable, and announced as "button" by screen readers
<div class="nav"><nav>Screen readers have a "skip to navigation" shortcut
<div class="main"><main>Screen readers have a "skip to main content" shortcut
<span class="heading"><h1>-<h6>Screen readers navigate by heading hierarchy
<div> + CSS grid<table> for tabular dataScreen readers have table navigation (row/column headers)

ARIA: When HTML Is Not Enough

Critical rule: No ARIA is better than bad ARIA. Only use ARIA when native HTML cannot express the semantics you need.

ARIA AttributeWhen to UseExample
aria-labelProvide an accessible name when no visible label exists<button aria-label="Close dialog">X</button>
aria-describedbyLink an element to its description<input aria-describedby="password-hint"> <span id="password-hint">Min 8 characters</span>
aria-expandedIndicate if a collapsible element is open<button aria-expanded="true">Section 1</button>
aria-liveAnnounce dynamic content changes<div aria-live="polite">5 results found</div>
role="alert"Important, time-sensitive notification<div role="alert">Your session will expire in 2 minutes</div>

Automated Testing in CI/CD

// axe-core: The accessibility testing standard
// Integrate into Jest, Playwright, or Cypress tests
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);

it('homepage should have no accessibility violations', async () => {
  const { container } = render();
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

// Playwright test
import { injectAxe, checkA11y } from 'axe-playwright';
await injectAxe(page);
await checkA11y(page); // Runs axe-core against the rendered page

Bottom line: Start with semantic HTML — it solves 80% of accessibility issues for free. Add automated a11y testing to CI/CD (axe-core) to catch regressions. Test manually with a keyboard (Tab through your entire app) at least once per feature. Accessibility is not a feature to add later — it is a property of good HTML. See also: CSS Framework Comparison and Responsive CSS in 2026.