agentsclimarketplace

A11y listbox

Skill xrnavigation/web-a11y-plugin/skills/a11y-listbox

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).From its SKILL.md

Install
npx -y skills add xrnavigation/web-a11y-plugin --skill a11y-listbox

Assembled 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.

SKILL.md

10.3 KB, ~2.6k tokens by cl100k_base, 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-combobox skill
  • 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

ComponentAttributeWhen
Containerrole="listbox"Always
Containertabindex="0"Always
Containeraria-labelledby or aria-labelAlways
Containeraria-multiselectable="true"Multi-select only
Containeraria-orientation="horizontal"Horizontal layout only (default is vertical)
Containeraria-activedescendantWhen using activedescendant focus management
Optionrole="option"Always
Optionaria-selectedAlways — every option needs true or false
Grouprole="group" + aria-labelWhen grouping options
Virtualizedaria-setsize + aria-posinsetWhen 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.

(APG Listbox Pattern)


4. Keyboard Interaction Summary

All listboxes

KeyBehavior
Down ArrowMove focus to next option
Up ArrowMove focus to previous option
HomeFocus first option (recommended for 5+ options)
EndFocus last option (recommended for 5+ options)
Type-aheadFocus matching option (recommended for 7+ options)

Single-select additions

KeyBehavior
SpaceSelect focused option
Down/Up ArrowMay also select (selection follows focus)

Multi-select additions (recommended model)

KeyBehavior
SpaceToggle selection of focused option
Shift + Down/UpMove focus and toggle selection
Shift + SpaceSelect contiguous range
Ctrl + ASelect/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:

  1. Keyboard reordering: e.g., Alt + Arrow keys to move selected item
  2. 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>

(APG Listbox Pattern)

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

What ships with it: 4 files

17.1 KB alongside SKILL.md

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.