Menus, listbox and combobox: accessible autocomplete
Build an accessible autocomplete with the combobox pattern: an input that controls a listbox, aria-activedescendant for the highlighted option, aria-autocomplete and aria-expanded, typeahead, and why role=menu is wrong for site navigation.
Comes up in: Web fundamentals, Machine coding, Frontend system design
Learn#
Farhan’s city picker shows suggestions as you type, but keyboard users cannot reach them, and screen readers say nothing when the list opens. It is a div list under an input.
A combobox is an input that controls a popup of suggestions, usually a listbox of options. Focus stays in the input while the user moves through options; aria-activedescendant tells assistive technology which option is highlighted:
<label for="city">City</label>
<input id="city" role="combobox" aria-autocomplete="list" aria-expanded="true"
aria-controls="city-list" aria-activedescendant="city-opt-0">
<ul id="city-list" role="listbox" aria-label="Cities">
<li id="city-opt-0" role="option" aria-selected="true">Bangalore</li>
<li id="city-opt-1" role="option" aria-selected="false">Banda</li>
</ul>The active descendant is the option that is highlighted while focus stays elsewhere. Enter selects it, Escape closes the list, and typing keeps working at all times.
Page
- input[aria-expanded=true]
- listbox: Bangalore, Banda, Banswara
State
empty
Console
empty
The user types "ban" in the city field. The listbox opens with Bangalore, Banda and Banswara; aria-expanded becomes true.
Read the steps as text
- The user types "ban" in the city field. The listbox opens with Bangalore, Banda and Banswara; aria-expanded becomes true.
- Down Arrow highlights Bangalore. Focus stays in the input; aria-activedescendant points at the option's id.
- The screen reader announces "Bangalore, 1 of 3". The user can still type, because focus never left the input.
- Enter selects Bangalore, fills the input and closes the list. Escape would have closed it without selecting.
Predict#
Predict first
A site's main navigation uses role="menu" and role="menuitem". What is wrong?
ARIA menus are for application command menus, like a desktop app’s File menu. Screen readers switch to a mode expecting arrow keys. Site navigation is a <nav> with a list of links; dropdowns inside it are disclosures.
Interview angle#
Say it like this — about 30 seconds
“An accessible autocomplete uses the combobox pattern. The input has role combobox, aria-expanded, aria-controls pointing at a listbox, and aria-autocomplete list. Options have role option and unique ids. Focus stays in the input; arrow keys move a highlighted option, and aria-activedescendant points at its id so screen readers announce it. Enter selects, Escape closes, and a live region can announce the number of results. I do not use role menu for this or for site navigation.”
Follow-up How do you build an accessible autocomplete or combobox?
Follow-up Why aria-activedescendant instead of moving focus to the option?
Follow-up What is typeahead in a listbox?
Practice#
On your own
Write the state machine behind an accessible autocomplete, and the aria-activedescendant value it produces.
Done when
- Typing opens the list when there are options; arrows move and wrap.
- Enter selects the active option and closes; Escape closes without selecting.
activeDescendantreturns the right id, or "" when closed or nothing is active.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
Which attribute points at the highlighted option while focus stays in the input?
aria-activedescendant.
What role does the suggestion popup usually have?
listbox, with option children.
Should site navigation use role="menu"?
No. Use nav with links; menu is for app command menus.
What do Enter and Escape do in a combobox?
Enter selects the active option; Escape closes the list.
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
aria-autocomplete="list" says suggestions appear in a popup; "both" also completes text inline. Keep aria-expanded in sync with the popup.
Check yourself: What does aria-autocomplete="list" mean?
The input shows a list of suggestions.
How it works underneathSenior rounds
Option ids must be unique on the page, since aria-activedescendant references them. Mark the highlighted option with aria-selected="true" and keep it scrolled into view Source: WAI-ARIA APG: Combobox pattern.
Check yourself: Why must each option have a unique id?
aria-activedescendant refers to the option by id.
Edge cases and trapsWhere people slip
Check yourself: How do you announce how many results appeared?
A polite live region, such as "5 results available".
In productionStaff-level answers
role="menu" only for app-style command menus Source: WAI-ARIA APG: Menu and menubar pattern. For short fixed lists, a native <select> beats any custom widget.Check yourself: When should you use a native select instead?
When there is a short, fixed list of options and no filtering.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- How do you build an accessible autocomplete/combobox?