Blog · 10 min read
Test JAWS, NVDA, and VoiceOver: Combobox Accessibility for Developers

An accessible combobox pairs the correct ARIA role and states (aria-expanded, aria-controls, aria-autocomplete) with full keyboard support, keeps DOM focus on the input, and uses aria-activedescendant to track the active option. Every attribute must update in sync with the widget’s real state, and the result has to be verified with actual screen readers (JAWS, NVDA, VoiceOver), not just automated checks. Skipping that manual pass is how comboboxes quietly break for the people who depend on them most.
TL;DR:
- Using
aria-activedescendantrequires consistent ID management and should be tested across different screen readers to prevent focus loss issues.- A native
<select>or<datalist>should be preferred unless custom filtering or rich formatting cannot be achieved otherwise.- The combobox role must always keep
aria-expanded,aria-controls, andaria-autocompleteattributes in sync with its real state, verified with manual screen reader testing.- Keyboard navigation must precisely follow expected patterns, including arrow keys, Enter, Escape, Home, End, and tab, to ensure accessibility compliance.
- Regular, comprehensive testing with screen readers like JAWS, NVDA, and VoiceOver is essential to identify dynamic focus issues and ensure reliable voiceover announcements.
Table of Contents
- 1. Required ARIA attributes and the combobox role
- 2. Managing focus with aria-activedescendant and keeping DOM focus on the input
- 3. Keyboard interaction matrix and expected behavior
- 4. Screen reader and browser compatibility: testing checklist and known quirks
- 5. Common implementation pitfalls and debugging checklist
- 6. AccessWiser perspective: trade-offs and conservative recommendations
- How AccessWiser helps teams catch combobox issues before users do
- FAQ
- Sources
1. Required ARIA attributes and the combobox role
The combobox role tells assistive technology that an input is paired with a popup, whether that popup is a listbox, grid, or dialog. That relationship only works when a handful of attributes stay accurate at every moment, not just at page load. The W3C APG combobox pattern lays out the baseline, and MDN’s combobox role reference covers the attribute values in detail.
A working implementation needs:
- Labeling:
aria-labelledbyoraria-labelso screen readers announce what the field is for. - aria-expanded: set to
truethe instant the popup opens andfalsethe instant it closes, never left stale. - aria-controls: pointing to the popup element’s own ID, so assistive technology can find and read it.
- aria-autocomplete:
nonefor a closed list of choices,listwhen typing filters suggestions, andbothwhen the input also auto-completes inline.
Before reaching for any of this, ask whether a native <select> or <input> with <datalist> actually meets the interaction requirements. Native controls carry keyboard handling, focus management, and screen reader support built in, with none of the attribute bookkeeping above. Custom ARIA comboboxes earn their complexity only when the design needs something native elements cannot do, like asynchronous filtering or rich result formatting. The APG pattern itself recommends “No ARIA” over “Bad ARIA” precisely because browser and assistive technology support for composite widgets still has gaps that require deliberate testing before shipping.
2. Managing focus with aria-activedescendant and keeping DOM focus on the input
A combobox has to do something unusual: let a user arrow through a list of options while the browser’s actual focus never leaves the text input. That separation between visual focus and DOM focus is exactly what aria-activedescendant is for. The input keeps real keyboard focus, and aria-activedescendant simply points to the ID of whichever option is currently highlighted, so screen readers announce that option without a focus event ever firing. Both MDN and WAI-ARIA 1.2 describe this as the intended pattern for composite widgets like combobox and textbox.
Getting it right in code means handling a short sequence correctly every time:
- On Down or Up arrow, move the visual highlight to the next or previous option.
- Update
aria-activedescendanton the input to the new option’s ID immediately. - Scroll that option into view if the popup can scroll, so sighted keyboard users never lose track of it.
- On selection or Escape, clear or reset
aria-activedescendantas appropriate.
Caret behavior is one of the sharper edge cases here: some assistive technologies stop tracking the text caret properly when aria-activedescendant is applied to certain input types, which is why a handful of implementations wrap the combobox in a container role with a role="textbox" child instead of attaching the attribute directly. Support for aria-activedescendant is not perfectly uniform across every AT and browser pairing, so treat the fallback pattern as a real option, not a last resort.
Pro Tip: Log every aria-activedescendant update to the console during development; a missing or orphaned ID is the single most common cause of a screen reader going silent on arrow key presses.
3. Keyboard interaction matrix and expected behavior
Keyboard behavior is where most combobox implementations drift from the spec, usually because a developer handles the common cases and skips the edge ones. The APG listbox-combo example documents the full expected matrix, and it is worth implementing exactly as written rather than improvising.
| Key | Expected behavior |
|---|---|
| Down Arrow | Moves visual focus to the next option; updates aria-activedescendant |
| Up Arrow | Moves visual focus to the previous option; updates aria-activedescendant |
| Alt+Down | Opens the popup without changing the current selection |
| Enter | Commits the highlighted option and closes the popup |
| Escape | Closes the popup and restores the value the input had before opening |
| Home | Moves to the first option (list open) or the start of the text (editable field) |
| End | Moves to the last option (list open) or the end of the text |
| Printable characters | Filter or auto-complete the list in an editable combobox |
| Tab / Shift+Tab | Move focus out of the widget entirely, never cycle within it |
That last row matters more than it looks: a combobox that traps Tab inside itself breaks the basic promise of keyboard navigation, and it is a frequent regression when developers add custom key handling without testing the full tab sequence afterward.
4. Screen reader and browser compatibility: testing checklist and known quirks
Automated checks catch missing attributes, not broken behavior, so a real test pass across screen reader and browser combinations is not optional for a combobox. A minimal matrix covers JAWS with Chrome, NVDA with Firefox, VoiceOver with Safari, and at least one mobile screen reader and browser pairing.
For each combination, verify:
- The input’s role and label announce correctly on focus.
- Opening and closing the popup is announced, and
aria-expandedmatches reality. - Arrow navigation announces each option as it becomes active.
- Selection commits correctly on Enter, and Escape restores the prior value.
- Typing filters the list, and any live-region announcement of result counts is sparing, not chatty.
WebAIM’s testing notes on VoiceOver document real divergence: VoiceOver often uses different interaction keys than JAWS or NVDA and does not always expose a popup the same way in reading mode. Treat these as implementation constraints, not bugs to wait out. Automated scanners remain useful for catching structural regressions at scale, but they consistently miss dynamic focus-management failures, which is exactly why manual testing stays the deciding step for this widget.
Pro Tip: Script your manual SR test cases once and rerun them after every combobox change, the same way you would rerun a unit test suite.
5. Common implementation pitfalls and debugging checklist
Most combobox bugs trace back to a small set of repeat offenders: aria-expanded left stale after a popup closes, aria-controls pointing to an ID that does not exist, disabled or removed options that still get referenced by aria-activedescendant, and DOM reparenting that silently switches a combobox out of the mode a screen reader expects.
When a combobox breaks, work the problem in order:
- Reproduce the failure in the specific screen reader and browser combination where it was reported.
- Inspect the live ARIA attribute values in the accessibility tree, not just the source markup.
- Confirm DOM focus is still on the input, not on the popup or an option.
- Verify
aria-activedescendantpoints to an ID that currently exists in the DOM. - Walk through every keyboard binding in the matrix above, one at a time.
- Run the same case through at least one screen reader before calling it fixed.
- Check that the active option is visibly scrolled into view and the caret position looks right.
When filing a bug, capture the exact attribute values at the moment of failure, the screen reader and browser versions involved, and the specific key sequence that triggered it. That record is usually the difference between a fix landing in one pass and a fix that gets re-opened a week later.
6. AccessWiser perspective: trade-offs and conservative recommendations
Our own bias runs conservative: reach for a native <select> or <datalist> first, and only build a custom ARIA combobox when the interaction genuinely requires it. Native elements carry years of accumulated browser and screen reader support that no amount of careful ARIA can fully replicate.
When a custom combobox is the right call, test it with real screen readers before it ships, not after a user reports it broken. Document every known AT quirk you accept rather than hope nobody notices. Pairing a scanner that watches for regressions with developers who understand the underlying pattern is what keeps a fix permanent instead of temporary.
— The AccessWiser Team
How AccessWiser helps teams catch combobox issues before users do
Custom widgets like comboboxes are exactly where automated scans earn their keep and exactly where they reach their limits, which is why we built AccessWiser to combine both. Scans can check sites against WCAG 2.2 AA, with Section 508 and EN 301 549 mappings, and flag code-level issues like a missing aria-controls mapping or a stale aria-expanded value, tied to the specific element and criterion involved. Every finding comes with plain-language guidance for fixing it directly in your code, so the fix holds rather than relying on a runtime patch.
Scheduled re-checks can catch it if a future deploy reintroduces the same combobox bug, and dated records of scans and fixes can provide documentation to show the work was done. If you want to build combobox testing into your regular development cycle, the pricing page outlines plans scaled to different page and scan needs.

FAQ
What is the correct ARIA role for a combobox?
The parent element takes role="combobox" and is paired with aria-expanded, aria-controls pointing to the popup’s ID, and an aria-autocomplete value of none, list, or both depending on the filtering behavior. The W3C APG pattern documents the full attribute set and the states each one must reflect.
When should I use aria-activedescendant instead of moving focus?
Use aria-activedescendant whenever DOM focus needs to stay on the text input while a screen reader announces a highlighted option in a popup, which is the standard pattern for composite widgets like combobox. WAI-ARIA 1.2 and MDN both describe this as the expected approach rather than moving real focus between options.
Should I build a custom combobox or use a native select?
Start with a native <select> or an <input> paired with <datalist> whenever the interaction allows it, since those carry built-in keyboard and screen reader support that a custom widget has to recreate by hand. A custom ARIA combobox is worth the added work only when the design needs something native elements cannot deliver, like live filtering against an external data source.
Which screen readers should I test a combobox with?
At minimum, test JAWS with Chrome, NVDA with Firefox, and VoiceOver with Safari, since each combination has shown different interaction patterns in practice, according to WebAIM’s testing notes. Add a mobile screen reader and browser pairing if the combobox ships on a responsive or mobile-first interface.
What causes a screen reader to go silent during combobox navigation?
The most common cause is aria-activedescendant pointing to an option ID that no longer exists in the DOM, often after a list re-renders without updating the reference. Checking the live accessibility tree, not just the markup, is the fastest way to catch this during debugging.
Sources
For implementation details beyond this guide, the primary sources are worth bookmarking directly. The W3C APG combobox pattern and its listbox-combo example remain the canonical reference for both the keyboard table and the focus-management approach. WAI-ARIA 1.2 defines the formal semantics behind every role and state mentioned here, while MDN’s combobox role page offers a more approachable attribute-by-attribute breakdown. For testing guidance and real-world screen reader behavior, WebAIM’s VoiceOver writeup and the broader Live Caption AI accessibility examples collection are useful starting points.
- Combobox Pattern | APG | WAI | W3C
- combobox role — MDN Web Docs
- Three things VoiceOver does differently — WebAIM
Articles on this blog are general information about web accessibility, not legal advice. Laws change and their application depends on your specific situation — for decisions with legal consequences, consult a qualified legal professional.
Articles on this blog are general information about web accessibility, not legal advice. Laws change and their application depends on your specific situation — for decisions with legal consequences, consult a qualified legal professional.