Reading WCAG won't make you fluent. Each playground below is a real, working comparison —
toggle, tap, and try the broken version to understand why the criterion exists.
WCAG 2.2 · 2.5.8Level AA
Target Size (Minimum)
Interactive targets must be at least 24 × 24 CSS pixels. Below the threshold,
users with motor disabilities, tremors, or touchscreens systematically miss.
Fails 2.5.8
A 16 × 16 px button. Hard to hit. Watch your error rate.
Hits: 0
Passes 2.5.8
A 44 × 44 px button. Comfortable, conformant, and beyond AAA.
Hits: 0
Pro tip: Apple HIG recommends 44 × 44 pt, Material recommends 48 × 48 dp.
The WCAG 2.2 floor of 24 × 24 px is the legal minimum, not the target.
WCAG 2.2 · 3.3.7Level A
Redundant Entry
Information previously entered in the same process must be auto-populated or selectable —
never re-typed. Helps users with cognitive disabilities and short-term memory loss.
WCAG 2.1 · 1.4.11Level AA
Non-text Contrast
UI components and meaningful graphics need a contrast ratio of at least 3:1
against adjacent colors. Borders, icons, states — not just text.
Create your account
Quick sign-up to continue.
Border ratio
1.2:1
Button ratio
1.4:1
Icon ratio
1.3:1
WCAG 2.1 · 1.3.5Level AA
Identify Input Purpose
Use HTML autocomplete tokens so browsers, password managers, and assistive tech understand each field's purpose.
Fails 1.3.5
No autocomplete. The browser can't help.
Not accessible
signin.html
<inputtype="text"placeholder="first thing">
No autocomplete token. Browser autofill, password managers, and assistive tech have no idea what this field is for.
Passes 1.3.5
Semantic autocomplete tokens. Try focusing each field — your browser will offer saved data.
Accessible · WCAG 1.3.5
signin.html
<inputtype="text"name="name"autocomplete="name">
The highlighted autocomplete="name" token tells browsers, password managers, and screen readers exactly what this field collects.
Common autocomplete tokens reference
nameFull name
given-nameFirst
family-nameLast
emailEmail
telPhone
street-addressAddress
postal-codeZIP / postcode
cc-numberCard number
bdayBirthday
current-passwordExisting pw
new-passwordNew pw
one-time-codeSMS/2FA code
Pattern library
Components, done right.
Production-ready patterns with the keyboard map, the accessible name, and the code in your framework.
Copy, adapt, ship.
<divrole="tablist"><buttonrole="tab"aria-selected="true"aria-controls="p1"id="t1">Overview</button></div><divrole="tabpanel"id="p1"aria-labelledby="t1">...</div>// Arrow keys cycle, only selected tab is in tab order
<divrole="status"aria-live="polite"aria-atomic="true">
Changes saved successfully
</div>// "polite" waits for the user to finish// Use "assertive" only for errors
Tooltip
1.4.134.1.2
This is dismissible & persistent
aria-describedbyRead after nameEscDismissible
<buttonaria-describedby="tip">Help</button><divid="tip"role="tooltip">
Click to share this article
</div>// WCAG 1.4.13: must be hoverable + persistent + dismissible
Skip Link
2.4.1
This page has a real one. Try pressing Tab from the address bar.
It's the first thing screen reader & keyboard users hit. It must be visible on focus.
TabFrom page top reveals it
<ahref="#main"class="skip">
Skip to main content
</a>/* Off-screen until focused */
.skip { position: absolute; top: -100px; }
.skip:focus { top: 1rem; }
Accordion / Disclosure
2.1.14.1.2
Free shipping on orders over $50. Delivery in 3–5 business days.
Space / EnterTogglearia-expandedRequired
<buttonaria-expanded="false"aria-controls="panel">
Shipping info
</button><divid="panel"role="region"hidden>
Free shipping…
</div>// Toggle: button[aria-expanded] + panel[hidden]// Native: <details><summary> is simpler if pure HTML
Menu / Dropdown
2.1.14.1.2
Duplicate
Archive
Delete
↓ / EnterOpen↑ ↓NavigateEscClose
<buttonaria-haspopup="menu"aria-expanded="false"aria-controls="m">Actions</button><ulid="m"role="menu"><lirole="menuitem"tabindex="-1">Duplicate</li></ul>// Note: a dropdown of LINKS is just a <ul> of <a> — no role="menu" needed
<labelfor="e">Email</label><inputid="e"type="email"requiredaria-invalid="true"aria-describedby="e-err"><pid="e-err">Please enter a valid email.</p><divrole="status"aria-live="polite">
Form has 2 errors. <ahref="#e">Fix email</a></div>// Validate on blur, not on every keystroke
Date input
1.3.53.3.24.1.2
MM/DD/YYYY
type="date"Native pickerautocompleteWCAG 1.3.5
<labelfor="d">Departure date</label><inputid="d"type="date"autocomplete="bday"aria-describedby="d-fmt"><pid="d-fmt">MM/DD/YYYY</p>// Native is almost always better than a custom calendar// Custom calendars: APG datepicker is the reference
Live tools
Test as you design.
Stop guessing. Pick colors, see contrast. Click any element, see its accessibility tree. No tab switching.
Contrast checker
Aa
The quick brown fox jumps over the lazy dog.
Ratio
21:1
Higher is better
Compliance
ARIA tree inspector
Click any element on this page to see its accessibility tree representation — what a screen reader actually receives.
Click any element to inspect it…
Health check
Paste any HTML — a component, a page, a snippet — and get a fast static analysis. Catches the obvious 80% (missing alt text, unlabeled inputs, contrast issues, heading order, missing landmarks). Not a substitute for a real audit, but it'll surface what an auditor would flag in the first 5 minutes.
Headings outline
The H1–H6 tree of this page, the way a screen reader navigates it. Skipped levels, missing H1, and empty headings are flagged. Click any heading to scroll to it.
Landmarks viewer
Toggle the overlay to see every landmark on this page — banner, navigation, main, complementary, contentinfo, search, region, form — labeled and outlined. This is the "page outline" screen reader users perceive when they jump by region.
Daily reference
The reference manual.
The four accessibility references web developers reach for most: ARIA attributes and roles,
keyboard interactions by widget, semantic HTML, and live regions.
ARIA reference
Every ARIA attribute and role you'll reach for, with what it does and when not to use it.
The first rule of ARIA: don't use ARIA if HTML can do it.
Keyboard interaction by widget
The keys each widget needs to support, distilled from the WAI-ARIA Authoring Practices.
If you're building a custom component, this is the contract.
Semantic HTML
The foundation. Get this right and you skip half the ARIA you'd otherwise need.
Landmarks
Landmarks are how screen reader users jump around a page. Use the HTML elements — they have implicit ARIA roles and need no extra markup.
Element
Implicit role
Use for
<header>
banner *
Top of the page: logo, primary nav, search. * Only when a direct child of <body> — otherwise just a section header.
<nav>
navigation
Major navigation blocks. Multiple allowed — label each: aria-label="Primary", "Footer", etc.
<main>
main
Primary content. One per page. Skip links target this.
<aside>
complementary
Sidebar, callout, related content — tangential to main flow.
<footer>
contentinfo *
Site-wide footer: copyright, legal, secondary nav. * Only as direct child of <body>.
<section>
region (only with name)
A generic section. Becomes a landmark only when given an accessible name via aria-labelledby or aria-label.
<search>
search
Search interface (HTML 2023). Replaces role="search" on <form>.
<form>
form (only with name)
Becomes a landmark only when given an accessible name.
Heading hierarchy
One <h1> per page describing the page's primary purpose.
No skipping levels. Don't go h1 → h3. Screen readers navigate the hierarchy and gaps confuse the structure.
Use heading levels for semantics, not size. If the visual design needs a small heading, use <h2 class="text-sm"> — not <h4>.
Don't use empty headings. If you need spacing, use CSS. Empty <h2></h2> is announced as "heading level 2, blank" — disorienting.
For SPA route changes that don't reload the page: move focus to the new <h1> (with tabindex="-1") so screen reader users know they've navigated.
Button vs link vs div
Element
When
Gets you
<button>
Triggers an action on the current page (open modal, submit form, toggle state).
Keyboard activation, Space+Enter, focus, role="button", correct AT announcement.
<a href>
Navigates somewhere — different page, anchor, external URL.
Enter activates (Space scrolls!), right-click context menu, "open in new tab".
<div role="button">
Almost never. Only if there's an extraordinary reason a real <button> won't work.
Nothing automatic. You add tabindex="0", Space/Enter handlers, disabled state — all the things <button> gives free.
Test: if "open in new tab" makes sense, it's a link. Otherwise it's a button.
Modern interactive elements
Element / attribute
What it does
<details> <summary>
Native disclosure widget. Keyboard, ARIA, animation — all free. Use this before reaching for custom JS.
<dialog>
Native modal element. showModal() handles focus trap, Esc-to-close, inert background automatically. Use this before building a custom modal.
popover attribute
Native popover (2024). For tooltips, dropdowns, and non-modal overlays. Pairs with popovertarget on the trigger.
inert attribute
Removes a subtree from the accessibility tree and keyboard focus. Use on backgrounds while a modal is open.
<input list>
Native combobox via <datalist>. Limited but adequate for simple cases.
The four ways to hide
Method
Visual
Screen reader
Keyboard
Use for
display: none
Hidden
Hidden
Skipped
Tabs (inactive panel), accordion (closed panel).
visibility: hidden
Hidden
Hidden
Skipped
Same as display:none, but reserves layout space.
aria-hidden="true"
Visible
Hidden
Focusable still!
Decorative icons next to text. Never on focusable elements.
.sr-only (CSS)
Hidden
Read
Skipped (usually)
Labels for icon buttons, status text, skip links until focused.
Live regions
How to announce things without moving focus. Among the most-misused parts of ARIA.
Decision matrix
What you're announcing
Use this
Why
Form saved, item added to cart, copied to clipboard
role="status"oraria-live="polite"
Non-urgent. Waits for the SR to finish its current sentence.
Form has errors, payment failed, session expiring
role="alert"oraria-live="assertive"
Urgent. Interrupts whatever the SR is reading.
Chat message arrived, log entry appended
role="log"
Sequential additions. SR knows order matters.
Elapsed time, countdown
role="timer"
SR can choose not to announce every tick.
Modal confirming a destructive action
role="alertdialog"
Combines alert urgency with dialog focus semantics.
The supporting attributes
Attribute
Default
What it controls
aria-atomic
false
When true: the SR reads the entire region on any change. When false: just the changed part. Default is fine for most cases.
aria-relevant
additions text
Which change types trigger an announcement: additions, removals, text, all. Don't override unless you have a specific reason.
aria-busy
false
Set true while updating a region to suppress announcements until done. Set back to false to flush.
Gotchas that will bite you
Live regions don't announce on initial render. The element must already exist in the DOM before the content changes. Render the empty region first, then update it.
Same content twice in a row may not re-announce. Screen readers deduplicate. To force re-announcement, briefly clear the region then set it again (or use a counter/timestamp in the text).
Moving focus interrupts announcements. If you announce and focus a new element, the focus change wins. For toast-style messages: don't move focus.
Assertive is for emergencies. Don't use it for "added to cart." Real users mute pages that constantly interrupt.
Visually hidden live regions still work. Use .sr-only if the announcement should be SR-only and not on screen.
Test in multiple SRs. NVDA, JAWS, VoiceOver, TalkBack all handle live regions slightly differently. The behavior you tested in one isn't the behavior in another.
Code review
Pre-merge checklist.
The questions to ask before approving any PR with UI changes. Keep this open during code review.
Progress saves locally.
Legal frameworks
The law, demystified.
WCAG is the technical standard. Section 508 and the ADA are the laws that adopt — and enforce — it
across different sectors.
Federal Law
Section 508
A 1998 amendment to the Rehabilitation Act of 1973. Requires federal agencies to
make their electronic and information technology (EIT) accessible to people with disabilities.
Who it covers
Federal agencies, their contractors, and any organization that develops, procures, or maintains technology used by the federal government.
The standard
The 2018 "ICT Refresh" formally adopted WCAG 2.0 Level A & AA. Many agencies are now voluntarily targeting WCAG 2.1 AA pending the next refresh.
Enforcement
Administrative complaints filed with the agency; civil suits permitted under §508(f). Federal employees may sue for damages and equitable relief.
Procurement teeth
Vendors must produce an ACR/VPAT (Accessibility Conformance Report / Voluntary Product Accessibility Template) to bid on federal contracts.
Civil Rights Law
ADA Title III
The Americans with Disabilities Act (1990). Title III prohibits discrimination by private
places of public accommodation. Courts increasingly include websites and apps in this definition.
Who it covers
Private businesses open to the public: retailers, restaurants, hotels, banks, healthcare, education, entertainment, and increasingly, all consumer-facing digital services.
The standard
No statutorily defined technical standard for the web (yet). The DOJ has repeatedly affirmed that WCAG 2.1 AA is the de facto compliance benchmark in litigation and settlements.
Enforcement
Private right of action — any individual can sue. The DOJ also enforces. Title III itself allows injunctive relief plus attorney's fees; many states (e.g. California's Unruh Act) add statutory damages of $4,000+ per visit.
Landmark case
Robles v. Domino's Pizza (9th Cir., 2019; cert. denied 2019) — confirmed Title III applies to websites and apps with a nexus to a physical place of accommodation.
Real cases. Real costs.
When accessibility isn't built in, courts decide what it costs. Selected landmark cases:
Robles v. Domino's Pizza
9th Circuit Court of Appeals · 2019
Outcome
Cert. denied by Supreme Court — confirmed ADA Title III applies to websites and apps with a nexus to a physical place of accommodation.
The claim
Blind plaintiff couldn't order pizza via Domino's website or mobile app with a screen reader.
Root cause
Missing alt text, unlabeled form fields, custom JS controls without ARIA — the basics.
NFB v. Target
N.D. Cal. · 2008 (settlement)
$6,000,000Settlement
First major ruling that retail e-commerce sites are subject to the ADA. Target paid $6M + adopted WCAG-equivalent standards.
Root cause
Inaccessible image maps, no keyboard navigation, missing alt text on product photos.
NY AG v. Sephora
New York · 2022 (settlement)
$2,000,000Penalty + remediation
Sephora.com inaccessible to blind users; required to bring site to WCAG 2.1 AA and audit annually.
Root cause
Color-only error states, missing form labels, focus traps in product carousel.
Andrews v. Blick Art Materials
E.D.N.Y. · 2017
Outcome
Federal judge held the ADA applies to standalone commercial websites — no physical nexus required.
Significance
Used in subsequent suits as precedent for "pure" e-commerce. Opened the door to thousands of filings.
The trend: ADA digital accessibility lawsuit filings surpassed 4,500 in 2023 — up from under 100 in 2014. The plaintiff's bar has industrialized this. Average defense cost: $250K–$1M even if you win.
At a glance
Dimension
Section 508
ADA Title III
Scope
Federal government & contractors
Private public accommodations
Adopted standard
WCAG 2.0 AA (ICT Refresh)
WCAG 2.1 AA (de facto)
Enforcement
Agency complaints, §508(f) suits
Private suits + DOJ
Penalties
Contract loss, remediation orders
Injunctions, fees, state damages
Documentation
VPAT / ACR required
Audit report recommended
Quick reference
What's new in WCAG 2.2.
Published October 2023. Nine new success criteria, all focused on cognitive disabilities,
motor accessibility, and authentication friction.
2.4.11
Focus Not Obscured (Minimum)
Level AA · The focused element must not be entirely hidden by other content.
Sticky headers and cookie banners are the usual culprits. When a user tabs to an element, at least part of the focused element must remain visible. The strict AAA version (2.4.12) requires fully visible.
2.4.13
Focus Appearance
Level AAA · Focus indicators must be at least 2 px thick with a 3:1 contrast ratio.
The default browser focus ring rarely passes. This dashboard uses a 4 px outline with high contrast — try tabbing through it to see.
2.5.7
Dragging Movements
Level AA · Any drag operation must have a single-pointer alternative.
Kanban boards, signature pads, range sliders — provide buttons or text input as alternatives.
3.3.8
Accessible Authentication (Minimum)
Level AA · No cognitive function test without an alternative.
Don't force users to memorize, transcribe, or solve puzzles. Allow paste, support password managers, and offer alternatives like passkeys, magic links, or biometrics.
Mobile a11y
Mobile is different.
Touch targets, orientation, reflow, motion. The criteria desktop audits skip get audited harder on mobile.
2.5.5 / 2.5.8
Touch target size
iOS HIG: 44pt. Material: 48dp. WCAG 2.2 AA floor: 24×24 CSS px. Always design to the platform max, not the WCAG floor.
1.3.4
Orientation
Don't lock to portrait or landscape. Some users mount their device permanently in one orientation.
1.4.10
Reflow
Content reflows to 320px CSS px wide without two-axis scrolling. Test in 320×256.
1.4.12
Text spacing
Layout must survive 200% line height, 50% paragraph space, 16% letter space — without clipping or overlap.
2.5.4
Motion actuation
If shake-to-undo exists, also offer a button. Motion-only triggers exclude users with tremors or mounted devices.
1.4.4
Text scaling
Respect iOS Dynamic Type and Android font scale. Use scalable units, not absolute px for body text.
2.5.1
Pointer gestures
Anything done with a swipe/pinch must also work with a single tap (or button). Don't gate features behind gestures.
Spacing
Target spacing
Adjacent touch targets need 8px+ gap to prevent mis-taps for users with motor impairments. WCAG 2.5.8 partly covers this via "spacing".
All 87 criteria
The complete WCAG index.
Every success criterion across WCAG 2.0, 2.1, and 2.2 — searchable, filterable, in plain English.
Tap any number to jump to the W3C definition.
Level
Version
Principle
0 of 87
No criteria match those filters.
Test yourself
Quick check.
Six questions. Real scenarios. Instant feedback with the WCAG citation that proves the answer.