Keep an Accessible Combobox Stable When Search Results Arrive Out of Order
The Problem
An accessible combobox can follow the correct ARIA pattern and still become unusable when two search responses arrive in the wrong order.
Reproducing the Race Condition
Reproduce this sequence:
- Type
ca, then quickly typecat. - The
catresponse arrives first and highlights cat facts. - The slower
caresponse arrives and replaces the list. aria-activedescendantnow points to an option that no longer exists.
IME Input Considerations
IME input adds another boundary: searching during composition can send partial text the user has not committed.
Modeling Request Generation
I would model the request generation explicitly:
let generation = 0;
let composing = false;
async function search(query: string) {
const mine = ++generation;
const options = await fetchOptions(query);
if (mine !== generation || composing) return;
render(options);
restoreActiveOptionByKey();
}
The Stable Key Matters
An array index cannot preserve the active option when ranking changes.
Regression Matrix
| Input | Injected failure | Expected evidence |
|---|---|---|
ca โ cat |
first request delayed | only cat results render |
| Arrow Down | result refresh | active key survives or resets visibly |
| Escape | response arrives afterward | popup stays closed |
| IME composition | network is fast | no request until compositionend |
Testing Recommendations
A Playwright test should assert:
- Focus remains on the input.
- Every
aria-activedescendantresolves to a live element. - Escape invalidates outstanding generations.
A manual screen-reader pass should confirm result-count announcements are not emitted for discarded responses.
The WAI-ARIA Authoring Practices combobox pattern defines the keyboard contract. The missing production step is testing that contract under asynchronous replacement, not only against static example data.
Which stale-response failure has been hardest to reproduce in your search UI?
Top comments (0)
Comments
No comments yet. Start the discussion.