Accessibility Suite
29 components, each with a real accessibility problem built in — low contrast, missing labels, broken keyboard navigation, and more. A practice ground for learning to find and test these issues.
Colour & Contrast
1. Status Indicators
Colour is the only signal for pass/fail, and the fail colour fails contrast requirements.
2. Disabled Button Text
Second contrast example: low-contrast text-on-background combination on a control users still need to read.
ARIA & Semantics
3. Contact Form
Inputs have no associated <label>; the error message is not linked via aria-describedby.
4. Custom Checkbox
Second ARIA example: a styled <span> acting as a checkbox with no role="checkbox", no aria-checked, not keyboard-focusable.
Keyboard & Focus
6. Tabs Widget
Second keyboard example: tabs respond to click only — no arrow-key navigation, no role="tab"/tablist, focus order is mouse-only.
Alt Text
7. Image Gallery
One image missing alt entirely, one meaningful image has alt="", one decorative image has unnecessary descriptive alt text.
Reviewing AI Output: The Gotchas
8. Modal Dialog
No role="dialog", no aria-modal, focus never moves in on open, Escape does not close it.
9. False Positive Trap
axe-core will likely flag this heading order as a violation (h2 → h4, skipping h3) — but visually and semantically for this isolated card, it causes no real harm to a screen reader user navigating by landmark. Good example for "don't blindly auto-fix everything the scanner reports."
Card Title
Card Subtitle
Some card body content that reads fine either way.
10. False Negative Trap
Passes axe-core cleanly — every field has a proper label and valid HTML. But CSS visually
reorders the fields (order property) so the screen-reader reading order doesn't
match what's shown on screen. No automated tool catches this.
Visually: Last name appears first. DOM order: First name first.
11. Cognitive Load Example
The real problem here is one no automated tool flags: the error message uses dense jargon with no plain-language explanation or recovery step — a genuine usability/cognitive barrier. Run a scan anyway and judge what it finds against what it misses.
Err 0x4F2: Input violates validation schema constraint on field pattern mismatch.
12. Dynamic Content Region
Content updates on click but has no aria-live region — screen reader users get no announcement.
13. Broken Skip Link
A "Skip to content" link is present (looks compliant at a glance) but its href target doesn't exist — silently broken for keyboard/screen-reader users who rely on it.
Capstone: Event Registration Flow
14. Event Registration (multi-step)
A 3-step registration journey with accessibility violations woven through it naturally. Your capstone: map the flow, find them, and build a full test suite that proves each one.
Review your registration:
Name: —
Ticket: —
More Colour & Contrast
15. Newsletter Signup
The email field has no <label> at all — only placeholder text, which disappears the moment the user types and fails contrast even before that.
16. Quick Actions
These buttons strip the default focus outline and never replace it — keyboard users tabbing through lose all visual track of where focus is.
17. Article Excerpt
The "Read the full write-up" link is the same colour as the surrounding paragraph text and has no underline — nothing distinguishes it as a link except position.
Our latest release notes cover three months of accessibility fixes across the platform. Read the full write-up for the complete changelog.
More ARIA & Semantics
19. Product Actions
A native <button> is overridden with role="link", and a list is stripped of its semantics with mismatched role="presentation" on individual items — both send assistive tech contradictory signals about what these elements are.
20. Payment Processing
Payment failures are written into an aria-live="polite" region. Errors this urgent need aria-live="assertive" or role="alert" — "polite" lets screen readers finish whatever they were doing first, so a failed payment can go unheard.
21. FAQ Accordion
Each trigger's aria-controls points at the wrong panel id — screen reader users are told they're controlling one panel while a completely different one actually opens.
More Keyboard & Focus
22. Order Details Panel
Once opened, Tab and Shift+Tab only cycle between the fields inside this panel — there's no Escape handler and no way to tab back out to the rest of the page.
Order Details
23. Plan Selector
Once this dropdown is open, its keydown handler calls preventDefault() on Tab — keyboard focus is stranded inside the widget and can never move on to the rest of the page.
- Starter — Free
- Pro — £19/mo
- Enterprise — Contact us
24. Payment Details
Explicit positive tabindex values override natural DOM order — keyboard users tab Name → CVV → Card Number → Expiry, nothing like the visual top-to-bottom order.
More Alt Text
25. Conference Photo
The image's accessible name repeats the visible caption word-for-word — a screen reader user hears the same sentence twice back to back for zero added information.
26. Favourite Item
A heart icon toggles favourite state, but the button has no aria-label, no visually-hidden text, and no title — its accessible name is empty.
27. Seasonal Promo Banner
This banner's message ("20% off — code SUMMER20") is baked entirely into a CSS background-image. There's no <img alt>, no visible text node, and no visually-hidden text anywhere — the content simply doesn't exist for a screen reader.
Composite Real-World Flows (Bonus)
28. Settings & Preferences Panel
A realistic settings screen with three violations woven in naturally: the notification toggle is a styled <span> with no role="switch"/aria-checked and isn't keyboard-focusable; the font-size steppers are icon-only spans with no accessible name and no keyboard support; and "Settings saved" is written as plain text with no aria-live announcement.
29. Search & Filter Results
A composite search-and-filter flow: the search button and the clear-filters button are icon-only <span>s with no accessible name and no keyboard support; the checkboxes have no <fieldset>/<legend> grouping them as a set; and the results count updates silently with no aria-live region to announce the change.
6 results