A11y listbox
Web accessibility agent skills — 23 cite-backed skills covering APG widget patterns, audit tooling, ARIA guidance, cognitive accessibility, and more. Works with Claude Code, Codex CLI, and Gemini CLI.
npx -y skills add xrnavigation/web-a11y-plugin --skill a11y-listboxAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Guides accessible listbox implementation for single and multi-select lists per APG patterns. Auto-invokes when creating listboxes, selection lists, option lists, or custom multi-select components. Covers required ARIA, keyboard interaction, single vs multi-select, and reorderable lists (WCAG 2.2 dragging alternatives).
SKILL.md
10.3 KB, as published. Nobody here has run it
Accessible Listbox Implementation
"It is highly recommended using the HTML select element... because there is a lot of keyboard interactivity to manage focus for all the descendants, and native HTML elements provide this functionality for you for free." — MDN: ARIA listbox role
A listbox presents a list of options and allows the user to select one or more. Every ARIA listbox you build is a promise to implement all keyboard interaction, focus management, and state updates yourself. Native <select> does this for free.
1. Decision: Native <select> vs ARIA Listbox
Use native <select> by default
Native <select> scored 100% success rate and 97.5% usability across all screen readers tested (24a11y, "Select Your Poison Part 2"). Custom ARIA implementations consistently scored lower.
Use ARIA listbox only when
- Options must contain images or complex content that
<select>cannot render (APG Listbox Pattern) - A dual-listbox transfer pattern or other composite widget is required
- Custom scrollable lists with non-text layouts are needed
Use combobox pattern instead when
- The dropdown needs filtering/autocomplete — see
a11y-comboboxskill - You need a custom-styled single-select that collapses to a button — use the APG select-only combobox, not a standalone listbox
Use checkboxes/radio buttons when
- Single-select with few options: radio button group
- Multi-select with few options: checkbox group
These outperform custom multi-select for usability (MDN; 24a11y).
2. Required ARIA Structure
Single-select listbox
<label id="color-label">Favorite color</label>
<ul role="listbox"
tabindex="0"
aria-labelledby="color-label">
<li role="option" id="opt-red" aria-selected="true">Red</li>
<li role="option" id="opt-blue" aria-selected="false">Blue</li>
<li role="option" id="opt-green" aria-selected="false">Green</li>
</ul>
Multi-select listbox
<label id="toppings-label">Toppings</label>
<ul role="listbox"
tabindex="0"
aria-labelledby="toppings-label"
aria-multiselectable="true">
<li role="option" id="top-cheese" aria-selected="true">Cheese</li>
<li role="option" id="top-peppers" aria-selected="false">Peppers</li>
<li role="option" id="top-onions" aria-selected="false">Onions</li>
</ul>
Required attributes summary
| Component | Attribute | When |
|---|---|---|
| Container | role="listbox" | Always |
| Container | tabindex="0" | Always |
| Container | aria-labelledby or aria-label | Always |
| Container | aria-multiselectable="true" | Multi-select only |
| Container | aria-orientation="horizontal" | Horizontal layout only (default is vertical) |
| Container | aria-activedescendant | When using activedescendant focus management |
| Option | role="option" | Always |
| Option | aria-selected | Always — every option needs true or false |
| Group | role="group" + aria-label | When grouping options |
| Virtualized | aria-setsize + aria-posinset | When not all options are in the DOM |
(APG Listbox Pattern; WAI-ARIA 1.2)
3. Single vs Multi-select
The architectural difference: in single-select, selection may follow focus (arrow keys both move focus and change selection). In multi-select, focus and selection are decoupled — moving focus must not change existing selections (APG Listbox Pattern).
// WRONG — selection follows focus in multi-select
function onArrowDown(listbox) {
focusNext();
if (listbox.getAttribute('aria-multiselectable') === 'true') {
selectFocused(); // Destroys existing selections!
}
}
// RIGHT — decouple focus from selection in multi-select
function onArrowDown(listbox) {
focusNext();
// In multi-select: focus moves, selection unchanged
// User must press Space to toggle selection
}
Focus landing behavior
- Single-select: Focus pre-selected option, or first option (which may auto-select)
- Multi-select: Focus first selected option, or first option. Never auto-select on focus.
4. Keyboard Interaction Summary
All listboxes
| Key | Behavior |
|---|---|
| Down Arrow | Move focus to next option |
| Up Arrow | Move focus to previous option |
| Home | Focus first option (recommended for 5+ options) |
| End | Focus last option (recommended for 5+ options) |
| Type-ahead | Focus matching option (recommended for 7+ options) |
Single-select additions
| Key | Behavior |
|---|---|
| Space | Select focused option |
| Down/Up Arrow | May also select (selection follows focus) |
Multi-select additions (recommended model)
| Key | Behavior |
|---|---|
| Space | Toggle selection of focused option |
| Shift + Down/Up | Move focus and toggle selection |
| Shift + Space | Select contiguous range |
| Ctrl + A | Select/deselect all |
For the complete keyboard spec including the alternative modifier-key model, see ${CLAUDE_SKILL_DIR}/references/keyboard-interaction.md.
5. Reorderable Lists (WCAG 2.2)
WCAG 2.5.7 Dragging Movements (Level AA) requires that all drag-and-drop functionality provide a single-pointer alternative that does not require dragging (WCAG 2.2 SC 2.5.7).
A keyboard alternative alone is insufficient — touch-screen users may not have a physical keyboard. You must provide both:
- Keyboard reordering: e.g., Alt + Arrow keys to move selected item
- Pointer-based non-drag alternative: visible up/down buttons, "move to position" menu, or numeric input
Compliant alternatives
<!-- RIGHT — visible move buttons satisfy SC 2.5.7 -->
<li role="option" aria-selected="true">
Item A
<button aria-label="Move Item A up">Up</button>
<button aria-label="Move Item A down">Down</button>
</li>
Note: The move buttons must be outside the role="option" element — interactive elements inside options are not valid. Use the Grid pattern if options need interactive children (APG Listbox Pattern).
(W3C Technique G219; Sparkbox)
6. Common Mistakes
6.1 Using ARIA listbox when native <select> suffices
<!-- WRONG — unnecessary complexity -->
<div role="listbox" tabindex="0">
<div role="option">Option 1</div>
</div>
<!-- RIGHT — native element, zero ARIA needed -->
<select>
<option>Option 1</option>
</select>
Native elements get keyboard interaction for free (MDN; APG).
6.2 Missing aria-selected on options in multi-select
<!-- WRONG — VoiceOver refuses to read options without explicit state -->
<ul role="listbox" aria-multiselectable="true">
<li role="option">Cheese</li>
<li role="option" aria-selected="true">Peppers</li>
</ul>
<!-- RIGHT — every option declares its selection state -->
<ul role="listbox" aria-multiselectable="true">
<li role="option" aria-selected="false">Cheese</li>
<li role="option" aria-selected="true">Peppers</li>
</ul>
When aria-multiselectable="true" is set, every option must have explicit aria-selected (MDN).
6.3 Interactive elements inside options
<!-- WRONG — links/buttons inside role="option" are not valid -->
<li role="option">
<a href="/details">Item A</a>
<button>Remove</button>
</li>
<!-- RIGHT — use Grid pattern for interactive children -->
<div role="grid">
<div role="row">
<div role="gridcell"><a href="/details">Item A</a></div>
<div role="gridcell"><button>Remove</button></div>
</div>
</div>
6.4 Missing aria-orientation on horizontal listboxes
The implicit orientation is vertical. Horizontal layouts must set aria-orientation="horizontal" explicitly, or arrow key expectations will be wrong (WAI-ARIA 1.2).
6.5 No visible focus indicator
The focused option must have a visible focus ring or highlight. Without it, sighted keyboard users cannot orient (WCAG 2.4.7).
6.6 Not scrolling focused option into view
In scrollable listboxes, programmatic scroll-into-view on focus change is required. Without it, the focused option becomes invisible to sighted users.
7. Cross-References
aria-decision-framework— decide whether you need ARIA at all (start here)a11y-combobox— for filterable/autocomplete dropdowns and styled select replacements
For detailed reference material:
${CLAUDE_SKILL_DIR}/references/keyboard-interaction.md— complete keyboard spec for both multi-select models${CLAUDE_SKILL_DIR}/references/common-mistakes.md— expanded mistake catalog with screen reader details${CLAUDE_SKILL_DIR}/references/screen-reader-behavior.md— JAWS, NVDA, VoiceOver behavior notes${CLAUDE_SKILL_DIR}/references/sources.yaml— provenance for all cited sources