Web Accessibility Practical Guide (WCAG 2.2)
Core accessibility principles, WCAG 2.2 criteria, and a practical checklist developers can apply right away.

Web accessibility is often framed as "for people with disabilities," but the scope is much wider. Someone holding their phone one-handed. A person reading their screen in bright sunlight. A keyboard-only user. Someone with a temporarily broken arm. An accessible website works better in all of these situations.
Why It Matters
Three reasons.
Legal requirements. In the US, ADA (Americans with Disabilities Act) lawsuits over inaccessible websites number in the thousands every year. The Domino's Pizza case — where the company lost because a visually impaired user couldn't use their site — set a major precedent. The EU's European Accessibility Act adds similar obligations. Regulations vary by country, but the trend is toward more enforcement, not less.
Business impact. Around 16% of the global population has some form of disability. Ignoring accessibility means losing those users. And accessible sites improve the experience for everyone else too.
SEO. Semantic HTML, proper heading structure, alt text, descriptive link text — following accessibility best practices naturally boosts SEO. Search engine crawlers interpret pages similarly to how a "user who can't see" would.
WCAG 2.2 Core — The POUR Principles
WCAG (Web Content Accessibility Guidelines) is the W3C's international web accessibility standard. Version 2.2 was published in 2023 and is built on four principles.
Perceivable
Users must be able to perceive the content. Information can't be conveyed solely through visuals.
- Provide alt text for images
- Add captions to videos
- Don't rely on color alone to convey meaning (consider color-blind users)
- Maintain sufficient color contrast
Operable
Users must be able to operate the interface. Without a mouse if necessary.
- All functionality available via keyboard
- Time limits on content must be adjustable
- Flashing content: 3 flashes per second maximum
- Clear navigation structure
Understandable
Content and interface behavior must be comprehensible.
- Specify page language (
langattribute) - Predictable interface behavior
- Clear guidance when input errors occur
- Consistent navigation
Robust
Content must be correctly interpreted by various technologies (browsers, assistive tech).
- Valid HTML markup
- Names, roles, and values programmatically determinable
- Status messages conveyed to assistive technology
Semantic HTML — The Foundation
Accessibility starts with semantic HTML. Building everything with div and styling it with CSS may look identical visually, but to a screen reader it's just a meaningless pile of text.
<!-- Bad -->
<div class="header">
<div class="nav">
<div class="nav-item" onclick="goHome()">Home</div>
<div class="nav-item" onclick="goAbout()">About</div>
</div>
</div>
<div class="main">
<div class="title">Accessibility Guide</div>
<div class="text">Content...</div>
</div>
<!-- Good -->
<header>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</header>
<main>
<h1>Accessibility Guide</h1>
<p>Content...</p>
</main>
The difference? A screen reader announces <nav> as "navigation" and lets users jump directly to <main>. <h1> communicates the page topic. With plain div elements and class names, all that structure is lost.
Commonly overlooked semantic elements:
<header>,<footer>,<main>,<aside>— page structure<nav>— navigation regions<article>— self-contained content units<section>— thematic grouping<button>— clickable actions (stop puttingonClickondivs)<time>— date/time information<figure>+<figcaption>— images with captions
ARIA — When Semantics Aren't Enough
ARIA (Accessible Rich Internet Applications) adds meaning that HTML alone can't express. Primarily needed for dynamic web applications.
But ARIA's first rule: if a native HTML element does the job, don't use ARIA.
Making a <div role="button"> when <button> exists means you have to manually implement keyboard events, focus handling, and more — things the native element gives you for free.
Cases where ARIA is genuinely needed:
<!-- Tab interface -->
<div role="tablist">
<button role="tab" aria-selected="true" aria-controls="panel1">Tab 1</button>
<button role="tab" aria-selected="false" aria-controls="panel2">Tab 2</button>
</div>
<div role="tabpanel" id="panel1">Tab 1 content</div>
<!-- Live region — announces dynamic content changes -->
<div aria-live="polite">42 search results found.</div>
<!-- Modal dialog -->
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title">
<h2 id="dialog-title">Settings</h2>
<!-- content -->
</div>
<!-- Label for icon-only button -->
<button aria-label="Close menu">✕</button>
aria-live="polite" tells the screen reader "when this region updates, announce it to the user." Useful for search result counts, form submission feedback, and similar dynamic updates.
Keyboard Navigation
Users who can't use a mouse navigate with keyboards. Tab moves between elements, Enter or Space activates them, Escape closes things, arrow keys navigate within lists.
Things to check during development:
Focus must be visible. Removing the default outline with outline: none is common but makes it impossible to see where focus is. If the default style doesn't fit your design, provide an alternative with :focus-visible.
/* Bad — completely removes focus indicator */
*:focus { outline: none; }
/* Good — shows focus ring only for keyboard users */
:focus-visible {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
Focus order must be logical. Tab follows DOM order, so visual order and DOM order should match. Rearranging layout with CSS while leaving the DOM in a different order breaks the tab sequence.
Focus trap in modals. When a modal is open, Tab shouldn't escape to elements behind it. Focus must cycle within the modal. Without this, users can tab to invisible elements behind the overlay.
Skip navigation. A "Skip to content" link at the top of the page lets keyboard users bypass the navigation instead of tabbing through every link on every page load.
Color Contrast
WCAG 2.2 color contrast ratios:
- Normal text: Minimum 4.5:1 (AA)
- Large text (18pt+, or 14pt bold): Minimum 3:1
- AAA level: 7:1 for normal text, 4.5:1 for large text
A surprising number of sites fail these checks. Light gray text on white backgrounds is the most common offender — it looks "clean" from a design perspective but is hard to read for many users.
Contrast checking tools:
- WebAIM Contrast Checker
- Chrome DevTools — the color picker in the Elements panel shows contrast ratios
- Figma plugins — Stark, A11y Color Contrast Checker
Common Mistakes
Missing alt on images. The most frequent issue. Every <img> needs alt. Decorative images get empty alt (alt=""), which tells screen readers to skip them. Omitting the alt attribute entirely causes screen readers to read the filename instead.
<!-- Informational image -->
<img src="chart.png" alt="Quarterly revenue chart for 2025, 30% increase in Q4">
<!-- Decorative image -->
<img src="decorative-line.png" alt="">
Missing form labels. <input> without an associated <label> is common. Using placeholder as a label substitute is also poor practice — placeholders disappear once the user starts typing.
<!-- Bad -->
<input type="email" placeholder="Email address">
<!-- Good -->
<label for="email">Email address</label>
<input type="email" id="email" placeholder="name@example.com">
Vague link text. "Click here" and "Read more" are meaningless without context. Screen reader users sometimes navigate by pulling up a list of all links on a page — ten links all saying "Click here" are indistinguishable.
Autoplaying media. Sudden audio is annoying for everyone, but especially problematic for screen reader users — it overlaps with the screen reader's voice output. Autoplay should be off by default.
No pause for animated content. Auto-scrolling carousels, marquee text, and animations must have a pause or stop mechanism. WCAG 2.2.2 requirement.
What's New in WCAG 2.2
A few notable additions:
Focus Not Obscured (2.4.11, AA) — Focused elements must not be hidden behind sticky headers, cookie banners, or other overlapping UI. Sticky headers covering focused elements is a common problem that now has an explicit criterion.
Dragging Movements (2.5.7, AA) — Drag-and-drop can't be the only way to perform an action. Alternatives (buttons, keyboard controls) must exist. Applies to kanban boards, sortable lists, and similar UI patterns.
Target Size (2.5.8, AA) — Click/touch targets must be at least 24x24 CSS pixels. Addresses the frustration of tiny buttons on mobile devices.
Start Accessible from Day One
Retrofitting accessibility at the end of a project is painful. It sometimes requires restructuring code or redesigning components entirely.
Starting with semantic HTML, testing with a keyboard, and checking color contrast from the beginning is far easier. Accessibility isn't about adding a feature — it's about doing web development correctly. Use HTML for its intended meaning, implement interactions according to standards, and a significant portion of accessibility is covered automatically.
Testing Tools
Automated:
- Lighthouse — built into Chrome DevTools, gives accessibility scores with specific fix suggestions
- axe DevTools — browser extension, more detailed than Lighthouse, distinguishes between auto-detectable and manual-check items
- eslint-plugin-jsx-a11y — catches JSX accessibility issues at build time
Automated tools catch roughly 30–40% of accessibility issues. The rest requires manual testing.
Manual:
- Navigate the entire site using only a keyboard. Is the tab order natural? Does every feature work?
- Zoom to 200% and 400%. Does the layout break?
- Test with a screen reader (VoiceOver, NVDA, JAWS)
Screen reader testing is the most revealing, though the learning curve is steep. macOS VoiceOver (Cmd+F5 to toggle) requires no installation and is a reasonable starting point.