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.

Please enter a valid email

4. Custom Checkbox

Second ARIA example: a styled <span> acting as a checkbox with no role="checkbox", no aria-checked, not keyboard-focusable.

Subscribe to updates

Keyboard & Focus

5. Dropdown Navigation Menu

Menu opens on :hover only — not reachable or operable by keyboard, no aria-expanded.

6. Tabs Widget

Second keyboard example: tabs respond to click only — no arrow-key navigation, no role="tab"/tablist, focus order is mouse-only.

Overview
Details
Reviews
Overview content.
Details content.
Reviews content.

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.

No updates yet.

Capstone: Event Registration Flow

Capstone

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.

1. Your Details 2. Ticket Type 3. Confirm

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.

More ARIA & Semantics

18. Save Preferences

This button is marked aria-hidden="true" but is still a real, focusable <button> — screen reader users can tab to a control that's supposed to be invisible to them, and hear nothing.

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.

No payment attempted yet.

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.

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.

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.

Team photo at the WDU conference 2025

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.

[background-image banner — no text alt]

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.

Email notifications
Font size
16px +

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