Overview
The combobox is one of the most complex ARIA patterns. It combines a text input with a popup listbox, allowing users to type to filter options and select from the results. The most common mistake is building a custom dropdown with no ARIA roles, no keyboard navigation, and no live announcements — leaving screen reader and keyboard users unable to operate the widget.
WCAG Criteria:
- 4.1.2 Name, Role, Value — the input must expose its role (
combobox), expanded state, and active descendant - 1.3.1 Info and Relationships — the listbox options must be programmatically associated with the input
- 2.1.1 Keyboard — arrow keys, Enter, and Escape must all be handled
Key requirements:
- DOM focus stays on the input —
aria-activedescendantprovides virtual focus in the popup - The input needs
role="combobox",aria-autocomplete="list",aria-expanded, andaria-controls - The popup list needs
role="listbox"withrole="option"children - Must handle: Arrow Down/Up to move through options, Enter to select, Escape to close
- Always announce result count changes via an
aria-live="polite"region - Browser does not auto-scroll
aria-activedescendanttargets — JS must callscrollIntoView
Autocomplete Combobox
Accessible Autocomplete vs. Unsemantic Dropdown
Inaccessible
<!-- No ARIA roles, no keyboard nav, no live announcements -->
<label>Choose a fruit</label>
<input type="text" placeholder="Type to search..."
onclick="showDropdown()"
oninput="filterItems(this.value)">
<div class="dropdown-list">
<div onclick="selectItem('Apple')">Apple</div>
<div onclick="selectItem('Banana')">Banana</div>
<div onclick="selectItem('Cherry')">Cherry</div>
<div onclick="selectItem('Date')">Date</div>
</div>Live Preview
Accessible
<label id="fruit-label" for="fruit-input">Choose a fruit</label>
<input id="fruit-input"
type="text"
role="combobox"
aria-autocomplete="list"
aria-expanded="false"
aria-controls="fruit-listbox"
aria-activedescendant=""
aria-labelledby="fruit-label"
autocomplete="off">
<ul id="fruit-listbox" role="listbox" aria-label="Fruits" hidden>
<li role="option" id="opt-apple">Apple</li>
<li role="option" id="opt-banana">Banana</li>
<li role="option" id="opt-cherry">Cherry</li>
<li role="option" id="opt-date">Date</li>
<li role="option" id="opt-elderberry">Elderberry</li>
</ul>
<span role="status" aria-live="polite" class="sr-only">
5 results available
</span>
<script>
// On input: filter options, update aria-expanded
// Arrow Down/Up: move aria-activedescendant, scrollIntoView
// Enter: select current option, close listbox
// Escape: close listbox
// On filter change: update aria-live status text
</script>Live Preview
What’s wrong with the unsemantic dropdown?
- The input has no
role="combobox"— screen readers don’t identify it as a searchable dropdown - The dropdown
<div>items have norole="option"— they are invisible to assistive technology - No
aria-expandedmeans the user can’t tell if the list is open or closed - No
aria-activedescendantmeans there is no virtual focus indicator for screen readers - Arrow keys don’t work — keyboard-only users cannot navigate the options
- No live region announces how many results are available after filtering
What the screen reader announces:
| Version | Announcement |
|---|---|
| Inaccessible (plain input) | “Choose a fruit, edit text” — no combobox context, no options announced |
| Accessible (combobox) | “Choose a fruit, combobox, collapsed, edit text” |
| When expanded | ”Choose a fruit, combobox, expanded, edit text” |
| Navigating options | ”Apple, 1 of 5” (via aria-activedescendant) |
| After filtering | ”3 results available” (via aria-live region) |