Accessible Components & Patterns
🧩 Accessible Components & Patterns
The ARIA Authoring Practices Guide
A W3C resource documenting the correct keyboard model, roles, and states for common UI patterns. Checking the APG's pattern for a component is the right starting point when building from scratch — it exists precisely because these patterns require hand-built ARIA and JavaScript, exactly Chapter 3's "no native element covers this" case.
🪟 Component 1: Accessible Modal
Completing Chapter 4's Focus Trap
role="dialog" plus aria-modal="true" announces this as a modal dialog; aria-labelledby gives it an accessible name from its own heading. Combined with Chapter 4's three-step focus management (move in, trap, restore) and an Escape-key handler to close it, this is a genuinely complete reference implementation.
🔽 Component 2: Accessible Dropdown/Combobox
aria-activedescendant
Tracks which option is currently highlighted as arrow keys move through the list — without moving actual DOM focus away from the input. Real focus stays on the input the entire time.
Full Keyboard Model
Arrow keys navigate options, Enter selects the highlighted one, Escape closes the list — none of this comes from the roles alone.
📑 Component 3: Accessible Tabs
Completing Chapter 3's role="tablist" example:
Roving Tabindex
Only the active tab has tabindex="0"; every other tab has tabindex="-1" — one Tab press enters the whole tablist, and Left/Right arrow keys move the "roving" focus between individual tabs, not Tab itself. This is the general APG convention across list-like widgets, not just tabs.
Improvising vs Following the APG
Improvised
Guessing at roles and keyboard behavior invites inconsistency with what users expect from every other tab/combobox/dialog they've ever used.
APG Pattern
A documented, battle-tested reference for exactly this component — less to get wrong, and consistent with the rest of the web.
💻 Coding Challenges
Challenge 1: Add Escape-to-Close to the Modal
Write the keydown handler that closes the modal from Component 1 when Escape is pressed, and correctly restores focus per Chapter 4's third step.
Goal: Practice completing the modal pattern with the missing keyboard piece.
Challenge 2: Implement Roving Tabindex for Arrow Keys
Write the JavaScript for the tabs component that, on Right Arrow, moves tabindex="0" and aria-selected="true" to the next tab (setting the previous one to tabindex="-1"/aria-selected="false") and focuses it.
Goal: Practice implementing the roving tabindex keyboard behavior, not just the static ARIA markup.
Challenge 3: Explain Why Focus Stays on the Combobox Input
Explain why the combobox pattern uses aria-activedescendant to track the highlighted option instead of simply moving real DOM focus to each option as the user presses arrow keys.
Goal: Practice articulating the reasoning behind this specific, non-obvious pattern.
This is Chapter 3's core lesson resurfacing specifically for these more complex components: it's entirely possible to add role="tablist"/role="tab" correctly, get every accessible name right, and still ship a broken experience by forgetting the arrow-key navigation the pattern requires — leaving only default Tab behavior, which cycles through every tab individually rather than the expected roving-tabindex model. The roles and states are only half of any APG pattern; the documented keyboard interaction model is the other half, and skipping it produces a component that looks correct in an accessibility tree inspection while still being genuinely broken to actually use.
🎯 What's Next
Every technique and component pattern comes together in the final chapter: Capstone: Auditing and Fixing an Inaccessible Page — walking a deliberately inaccessible example page through every fix from the course.