agentsclimarketplace

A11y combobox

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

Guides accessible combobox and autocomplete implementation per APG patterns. Auto-invokes when creating combobox, autocomplete, typeahead, select-with-search, or custom dropdown components. Covers required ARIA, keyboard interaction, and variant selection (select-only, editable, autocomplete modes).From its SKILL.md

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

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.6 KB, ~2.8k tokens by cl100k_base, as published. Nobody here has run it

Accessible Combobox Implementation

"No ARIA is better than Bad ARIA." — APG Read Me First

A combobox is a composite widget combining a named input with a popup that lets users set the input's value. The popup can be a listbox, grid, tree, or dialog. (APG Combobox Pattern)

This is one of the hardest widgets to get right. Screen reader support is inconsistent, aria-activedescendant is unreliable, and the keyboard interaction surface is large. Read this skill before building.


1. When to Use Native <select> vs Combobox

Use this decision tree before reaching for ARIA combobox.

SituationUseWhy
Single selection, < 15 items, no filtering needed<select>100% AT success rate, 97.5 usability score in testing. (24a11y study)
Simple autocomplete, inconsistent styling acceptable<datalist>Native, but broken on Android Firefox, iOS/iPadOS VoiceOver limited to 3 options. (Roselli)
> 15 options, need type-ahead filtering, closed listSelect-only comboboxCustom styling possible; caveat: WebKit bug with aria-activedescendant in VoiceOver/Safari. (APG)
Free text input + optional suggestionsEditable comboboxSearch, address lookup, autocomplete patterns. (APG)
Multi-select from listAvoid <select multiple>Only 25.3% AT success rate. Use a listbox with checkboxes instead. (24a11y study)

Do NOT use the ARIA 1.1 readonly-input combobox pattern — it is invisible to NVDA scan mode. (24a11y study)


2. Combobox Variants

Autocomplete Modes

Modearia-autocompleteBehavior
No autocompletenonePopup shows fixed suggestions (e.g., recent searches)
List (manual)listPopup filters to matches; user must explicitly select
List (automatic)listFirst match auto-highlighted; becomes value on blur
Inline + listbothFirst match auto-highlighted AND inline completion shown in input

Source: APG Combobox Pattern

Structural Variants

  1. Select-only — No text input; <div role="combobox">. Functionally replaces <select>. (Example)
  2. Editable + autocomplete="both" — Full inline + list filtering. (Example)
  3. Editable + autocomplete="list" — List filtering, manual selection. (Example)
  4. Editable + autocomplete="none" — Fixed suggestion list, no filtering. (Example)
  5. Grid popuprole="grid" popup for tabular suggestions. (Example)
  6. Dialog popuprole="dialog" popup (e.g., date picker). DOM focus moves into dialog. (Example)

3. Required ARIA Structure

Minimal Correct Markup (Editable + Listbox)

<!-- WRONG — missing aria-controls, no listbox role, no option roles -->
<input type="text" role="combobox">
<ul class="dropdown">
  <li>Option 1</li>
</ul>

<!-- RIGHT -->
<label for="fruit-input">Fruit</label>
<input
  id="fruit-input"
  role="combobox"
  type="text"
  aria-expanded="false"
  aria-controls="fruit-listbox"
  aria-autocomplete="list"
  aria-activedescendant=""
>
<ul id="fruit-listbox" role="listbox" hidden>
  <li id="opt-apple" role="option">Apple</li>
  <li id="opt-banana" role="option">Banana</li>
</ul>

Minimal Correct Markup (Select-Only)

<!-- WRONG — using readonly input (ARIA 1.1 pattern, invisible to NVDA scan mode) -->
<input type="text" role="combobox" readonly>

<!-- RIGHT -->
<label id="color-label">Color</label>
<div
  role="combobox"
  tabindex="0"
  aria-labelledby="color-label"
  aria-expanded="false"
  aria-controls="color-listbox"
  aria-haspopup="listbox"
  aria-activedescendant=""
>Red</div>
<ul id="color-listbox" role="listbox" hidden>
  <li id="opt-red" role="option" aria-selected="true">Red</li>
  <li id="opt-blue" role="option">Blue</li>
</ul>

Required Attributes Reference

ElementAttributeValueNotes
ComboboxrolecomboboxOn <input> or <div> (select-only)
Comboboxaria-controlsIDREFMust reference popup; present even when hidden
Comboboxaria-expandedtrue/falseReflects popup visibility
Comboboxaria-autocompletenone/list/bothOmit only if none
Comboboxaria-haspopupgrid/tree/dialogOnly when popup is NOT listbox (default)
Comboboxaria-activedescendantIDREFPoints to focused option; DOM focus stays on combobox
Popuprolelistbox/grid/tree/dialogMust match aria-haspopup
Optionsroleoption/gridcell/treeitemPer popup type
Optionsaria-selectedtrueOn currently focused/selected option

Source: APG Combobox Pattern, WAI-ARIA 1.2


4. Keyboard Interaction Summary

Abbreviated table. Full spec per variant: ${CLAUDE_SKILL_DIR}/references/keyboard-interaction.md

KeyEditable ComboboxSelect-Only (Closed)Select-Only (Open)
Down ArrowOpen popup, focus first/next optionOpen popupNext option
Up ArrowOpen popup (optional), focus lastOpen popupPrevious option
EnterAccept focused option, closeOpen popupAccept option, close
EscapeClose popupClose without selecting
TabMove focus outMove focus outAccept + close + move focus
Printable charsType in inputOpen + jump to matchJump to matching option
Home/EndMove cursor in inputFirst/last option
Alt+DownOpen without moving focusOpen popup

5. Common Mistakes

5.1 Treating aria-activedescendant as DOM Focus

<!-- WRONG — moving DOM focus to each option -->
<input role="combobox" ...>
<ul role="listbox">
  <li role="option" tabindex="0">Apple</li> <!-- Don't make options focusable -->
</ul>

<!-- RIGHT — DOM focus stays on input, aria-activedescendant points to option -->
<input role="combobox" aria-activedescendant="opt-apple" ...>
<ul role="listbox">
  <li id="opt-apple" role="option" aria-selected="true">Apple</li>
</ul>

aria-activedescendant is a screen reader semantic, not focus. Only one element has true focus (document.activeElement). Keyboard events fire on the focused element, not the active descendant. (Higley)

Exception: Dialog popups DO move real DOM focus into the dialog. They do NOT use aria-activedescendant. (APG)

5.2 Relying Solely on aria-activedescendant for Announcements

VoiceOver ignores aria-activedescendant changes when the input is empty (Safari) or when aria-selected="true" is missing (Chrome). NVDA fails to announce character deletions. Mobile screen readers essentially ignore it entirely.

Fix: Supplement with a hidden aria-live region for critical state changes (option count, selected value). React Aria had to adopt this workaround. (React Aria; Higley)

5.3 Using role="group" on Option Groups

<!-- WRONG — VoiceOver fails to announce focus on grouped options -->
<ul role="listbox">
  <li role="group">
    <ul>
      <li role="option">Apple</li>
    </ul>
  </li>
</ul>

<!-- RIGHT — flat list, use aria-label on options if grouping context needed -->
<ul role="listbox">
  <li role="option" aria-label="Fruit: Apple">Apple</li>
</ul>

Source: React Aria

5.4 Missing aria-controls When Popup Is Hidden

aria-controls must reference the popup element even when the popup is not visible. The referenced element must exist in the DOM. (APG Combobox Pattern)

5.5 Adding Screen Reader Control Hints

<!-- WRONG — screen readers already announce how to use a combobox -->
<input role="combobox" aria-description="Use arrow keys to navigate options">

<!-- RIGHT — let the screen reader provide its own instructions -->
<input role="combobox" ...>

Screen readers already tell users how to interact with standard controls. Custom instructions create conflicts, redundancy, and verbosity. (Roselli)

5.6 Auto-filtering Without User Request

The 24a11y study found that auto-filtering options without explicit user opt-in reduced success rates. Show the full list first; filter only on explicit interaction. (24a11y study)


6. Cross-References

  • aria-decision-framework — check whether you need ARIA combobox at all (use native <select> first)
  • ${CLAUDE_SKILL_DIR}/references/keyboard-interaction.md — complete keyboard spec per variant
  • ${CLAUDE_SKILL_DIR}/references/screen-reader-behavior.md — per-AT behavior differences and workarounds
  • ${CLAUDE_SKILL_DIR}/references/common-mistakes.md — expanded anti-patterns with citations
  • ${CLAUDE_SKILL_DIR}/references/sources.yaml — provenance for all cited sources

What ships with it: 4 files

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