A11y combobox
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-comboboxAssembled 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 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).
SKILL.md
10.6 KB, 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.
| Situation | Use | Why |
|---|---|---|
| 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 list | Select-only combobox | Custom styling possible; caveat: WebKit bug with aria-activedescendant in VoiceOver/Safari. (APG) |
| Free text input + optional suggestions | Editable combobox | Search, address lookup, autocomplete patterns. (APG) |
| Multi-select from list | Avoid <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
| Mode | aria-autocomplete | Behavior |
|---|---|---|
| No autocomplete | none | Popup shows fixed suggestions (e.g., recent searches) |
| List (manual) | list | Popup filters to matches; user must explicitly select |
| List (automatic) | list | First match auto-highlighted; becomes value on blur |
| Inline + list | both | First match auto-highlighted AND inline completion shown in input |
Source: APG Combobox Pattern
Structural Variants
- Select-only — No text input;
<div role="combobox">. Functionally replaces<select>. (Example) - Editable + autocomplete="both" — Full inline + list filtering. (Example)
- Editable + autocomplete="list" — List filtering, manual selection. (Example)
- Editable + autocomplete="none" — Fixed suggestion list, no filtering. (Example)
- Grid popup —
role="grid"popup for tabular suggestions. (Example) - Dialog popup —
role="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
| Element | Attribute | Value | Notes |
|---|---|---|---|
| Combobox | role | combobox | On <input> or <div> (select-only) |
| Combobox | aria-controls | IDREF | Must reference popup; present even when hidden |
| Combobox | aria-expanded | true/false | Reflects popup visibility |
| Combobox | aria-autocomplete | none/list/both | Omit only if none |
| Combobox | aria-haspopup | grid/tree/dialog | Only when popup is NOT listbox (default) |
| Combobox | aria-activedescendant | IDREF | Points to focused option; DOM focus stays on combobox |
| Popup | role | listbox/grid/tree/dialog | Must match aria-haspopup |
| Options | role | option/gridcell/treeitem | Per popup type |
| Options | aria-selected | true | On 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
| Key | Editable Combobox | Select-Only (Closed) | Select-Only (Open) |
|---|---|---|---|
| Down Arrow | Open popup, focus first/next option | Open popup | Next option |
| Up Arrow | Open popup (optional), focus last | Open popup | Previous option |
| Enter | Accept focused option, close | Open popup | Accept option, close |
| Escape | Close popup | — | Close without selecting |
| Tab | Move focus out | Move focus out | Accept + close + move focus |
| Printable chars | Type in input | Open + jump to match | Jump to matching option |
| Home/End | Move cursor in input | — | First/last option |
| Alt+Down | Open without moving focus | Open 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