Skip to main content
WCAGrules
Quick navigation

Guides · Interface patterns

Autocomplete, the Hardest Common Widget to Get Right

A field that suggests as you type has to narrate itself. Most of them stay silent.

Last reviewed August 30, 2026

A field that suggests as you type has to say three things out loud that a sighted user gets from a glance. A list just appeared. There are six things in it. You are on the third one. Miss any of the three and the widget is decorative.

It is also the widget most likely to have been built from a tutorial that is now wrong. The combobox pattern changed substantially in ARIA 1.2, because the earlier versions caused real implementation problems, and the change moved role="combobox" off a wrapper element and onto the input itself. Most of the combobox writing on the web still describes the old shape. If your code puts the role on a div around the field, that is why.

Try the Browser's Version, With Your Eyes Open

A native input with a list attribute pointing at a datalist gives you type-ahead with no JavaScript and nothing to wire up. It is worth trying for a small fixed set of options, and it comes with three documented problems you should know before you ship it.

  • The suggestions do not zoom. The list keeps its own size while the rest of the page grows.
  • You cannot style it, which means it will not follow a high contrast mode.
  • Some screen reader and browser pairs do not announce the suggestions at all, and the pair named in the documentation is NVDA with Firefox. That is the exact failure this page exists to prevent, so treat it as a real risk rather than a rough edge.

So datalist is a fair first try where the field would work without suggestions anyway, and a poor choice where the list is the only way to know what is allowed. Note also that no ARIA does not mean no ARIA semantics. The browser maps a list-attached input into the accessibility tree as a combobox regardless, which is why the failure mode is silence rather than confusion.

The Combobox Contract

When you build it yourself, the input is the combobox and the suggestion container is usually a listbox. Four things are stated as requirements in the specification, and one of them is missing from almost every implementation we test.

  • role="combobox" on the input, with aria-expanded true when the list is showing and false when it is not.
  • aria-controls on the input, pointing at the popup element.
  • aria-autocomplete on the input, and this is the one people miss. It is a requirement whenever the field completes as you type, and it takes one of three values. list when the popup offers values matching what has been typed. both when it does that and also completes the rest of the word inline after the cursor. none when the popup shows the same values whatever you type.
  • A popup with a real role, which may be listbox, tree, grid or dialog. A listbox is implied, so if you use any of the other three you also need aria-haspopup naming which one.

Two things that get forgotten because they are not about the popup. The combobox needs an accessible name, from a label element where the input can take one, and otherwise from aria-labelledby or aria-label. And the option the user is currently on takes aria-selected="true", so somebody arrowing through the list hears which one is live rather than only hearing its text.

The Keyboard, In Full

Arrow keys move through options and Enter selects is the version everybody implements, and it leaves out the key that opens the list in the first place.

  • Down Arrow opens the popup and moves into it. Without this there is no keyboard route into a list that has not appeared yet, which makes the whole field keyboard-inoperable however good the rest is.
  • Up and Down move through the options once the popup is open.
  • Left and Right do not. Right Arrow returns focus to the input without closing the popup and moves the text cursor one character right. Capture those keys for navigation and you have taken away the visitor's ability to edit what they typed.
  • Enter accepts the focused option, closes the popup, and puts the value in the field.
  • Escape closes the popup. Optionally, pressing it again on an editable combobox clears the field. Leaving what was typed is the safer default and clearing it is also sanctioned, so do not report the other behavior as a failure.
  • Alt with Down or Up are optional bindings that open the popup without moving focus, and close it or return focus to the input.

Above all, do not swallow the keys the browser uses for text editing. A combobox that intercepts Home, End, or the arrows for its own purposes has broken a text field to decorate it.

Where Focus Actually Lives

For the type-ahead field this page is about, real focus never leaves the input. aria-activedescendant on the input names the option currently highlighted, which moves a virtual cursor through the list while the person keeps typing. Move real focus into the list and they can no longer type, which defeats the point of the widget.

One exception, and it is the one that makes our own pages look like they disagree. Where the popup is a dialog rather than a listbox, DOM focus does move into it, because a dialog does not support the virtual cursor mechanism. That is the shape a date picker takes, so if you are building one of those, the rule above does not apply to you.

Announce the Results

A live region saying 6 results available covers the first two of the three things this page opened with, and it is exactly what 4.1.3 Status Messages asks for. The line to know is that the count is the status message and the list of results is not, so wrap the sentence rather than the container.

Do not announce on every keystroke

Debounce it. A polite live region does not interrupt, it queues, so firing on every letter does not produce a stream of interruptions. It produces a backlog, and a screen reader user hears a stale count for every letter they typed while the current one waits its turn. Wait for a pause in typing before you update the region, and use polite rather than assertive, because assertive is what actually cuts across whatever they were listening to.

The Mistakes We See Most

  1. Suggestions in a plain div. No role, no options, nothing announced. The list is visible and does not exist.
  2. No way to open the list from the keyboard. Everything works once the popup is showing and Down Arrow does nothing when it is not.
  3. Selection only on click. Enter submits the form instead of accepting the highlighted option, so the keyboard user posts a half-typed query.
  4. Results that replace what was typed as the user arrows through them, so they lose their own text.
  5. Suggestions that vanish on blur before a click registers, which is a mouse failure as well as a keyboard one.

Touch and Small Screens

On a phone the suggestion list competes with the on-screen keyboard for the same space. Two things follow. Options squeezed into a few pixels of height run into 2.5.8 Target Size when you drew them yourself, and do not when the browser drew them, because the criterion excepts a target whose size the user agent controls and the author has not modified. So your custom list has a 24 by 24 obligation and a native picker does not.

The other one has no criterion attached and is worth fixing anyway. A list rendered underneath the keyboard is unreachable, and no amount of correct ARIA helps. Test it on a real device rather than in a narrow browser window.

Common questions

Is a custom dropdown always worse than a native select?
Not always, and it is a much larger job than teams expect. A native select brings its own keyboard behavior, its own platform picker on mobile, and its own accessibility semantics free. A custom combobox needs a role, an expanded state, a controls relationship, an autocomplete value, an accessible name, a virtual cursor, and six keyboard bindings before it reaches parity. Build one when you genuinely need type-ahead over a long list, not to change the arrow icon.
What is the difference between the autocomplete attribute and an autocomplete widget?
Three things share the word. The HTML autocomplete attribute tells the browser what kind of personal information a field wants, so it can fill it in, and that is 1.3.5 Identify Input Purpose. An autocomplete widget is a field that suggests values as you type. And aria-autocomplete is a required ARIA property on that widget saying which kind of suggesting it does. They are unrelated jobs with one word between them.
Why does my combobox tutorial not match the specification?
Because the pattern changed in ARIA 1.2, after the earlier versions caused implementation problems. The most visible difference is that role="combobox" now goes on the input itself rather than on a wrapper around it, and the popup is referenced with aria-controls rather than aria-owns. If your code has the role on a div, it is following the old pattern.
Should focus move into the suggestion list?
Not for a type-ahead field. Real focus stays in the input and aria-activedescendant names the highlighted option, so the person can keep typing while they arrow. The exception is a popup built as a dialog, such as a date picker calendar, where DOM focus does move in because a dialog does not support the virtual cursor mechanism.

Sources

Keep reading

More on interface patterns

Reading about it is the cheap part.

Find out where your site actually stands. The free scan checks 10 pages in a real browser against all 90 supported automated rules, keeps its 27 best-practice checks separate from WCAG findings, and names the rule behind every finding. The full audit adds an expert review and a real blind screen-reader user. From $499, with the report in 5 business days on Rapid and 10 on Standard, and the clock starting at cleared payment.

Go somewhere useful

Find tools, resources and your workspace.

29 destinations