Building an Accessible Combobox

Oct 18, 2020


This post talks about how I implemented an accessible combobox widget, and all the hoops I jumped through.

The biggest hoop was the W3C ARIA community screws up on ARIA 1.1 combobox specs. ARIA 1.2 has a fix, but it is still in working draft.

Without knowing this, I went ahead to implement 1.1 combobox. It was costly to realize that screen readers have poor support for ARIA 1.1 comboboxes and I later had to switch to ARIA 1.2.

If you are building combobox / autocomplete like widgets and care about accessibility, this post can help you learn what’s ahead.

If you are researching accessible UI libraries, I also compare a few of them and their combobox implementations. The best one is Reach UI Combobox. It comes handy if you use React.

Finally, the code are hosted in a Github repo.

What is a combobox?

Quote from WAI-ARIA:

A combobox is an input widget with an associated popup that enables users to select a value for the combobox from a collection of possible values.

Below is a demo:

A video that demos a combobox UI widget. In the video, a user types in a state name in a text field. An associated popup shows up and lists the available states

A combobox consists of a few parts:

  • An input element
  • (Optional) A button that triggers the dropdown
  • A popup

The ARIA site has detailed specs about:

  • How to organize these HTML elements
  • What aria attributes they should have
  • How they should change.

One design goal is to make sure that it’s consistent between what sighted users see and what screen readers interpret. This is a difficult goal.

What are the accessibility problems with comboboxes?

If you are working on a legacy site that has a combobox like widget, chances are, the widget was not built with accessibility in mind. Specifically, we could see the following symptoms:

  • Keyboard interactions are not working (up/down arrow keys were not supported)
  • Screen readers are not able to read the input field correctly
  • Screen readers are not able to focus on the dropdown options and read properly
  • The HTML elements are not organized with semantic in mind, so screen readers have a difficult time to recognize and behave correctly

It’s likely because the current implementation is based on a select / dropdown library, e.g., select2. Even if it works perfectly for sighted users, the HTML structure doesn’t follow ARIA combobox spec.

They won’t work well with screen readers because:

Screen readers are strict about HTML element semantics and structures

The current web development practices are very relaxed about how to use HTML (non-)semantic elements.

We often allow using non-semantic elements: <div> and <span> to act like semantic elements like <form>, <table>, and <article>, without realizing that they should support certain behaviors.

On the other hand, modern browsers are lenient about these poorly structured HTML code. It is relatively easy to render the page visually with a variety of HTML / CSS tricks and make them behaves in certain way. As long as they can get past QA and product, they go to production.

As a result, we see a lot of issues including:

  • Loose, or even incorrectly structured, HTML documents
  • CSS effects that promote certain effects, or hacks around the visual components
  • Visual behaviors that natural to sighted users without too much explanations, but difficult for screen reader users (e.g., hover-and-expand navigation menus)
  • Most dev take the browser focus for granted, and rarely spend time to understand how browsers manage the focus
  • We use too much non-semantic elements like <div> to wrap any components without caring about its semantic.
  • We use div or <a> to build a button, without using the <button> tag itself.

Who would care if the HTML semantics are wrong?

Unfortunately, screen readers do.

One example is the button. Many sites use <div> or <a> and CSS tricks to make elements render like buttons on the screen.

Now when users complain about the widget isn’t accessible, as a dev, a natural thought is to add role="button" to the element and call it a day.

But assigning the button role to an element expects the element to have certain attributes and behaviors. You would need to respect all the specs and use all sorts CSS and JS to implement or approximate those behavors.

At the end of the day, you save more time by just using the <button> element which comes with all those semantics.

Quote from MDN:

A great deal of web content can be made accessible just by making sure the correct Hypertext Markup Language elements are used for the correct purpose at all times.

Combobox spec: ARIA 1.1 vs. ARIA 1.2

Okay, now you are aware of the importance of organizing HTML semantics. We might think that’s it, just following strictly the ARIA specs for combobox, and we should not worry about accessibility any more.

It is correct to some extent, until it is not.

As of writing, ARIA 1.1 is still the recommended spec while 1.2 is still in working draft. So ARIA 1.1 should be the accepted standard.

The ARIA site also comes with reference implementations with source code, which should answer any implementation questions we have.

So I went ahead to implement the ARIA 1.1 combobox. Then when we tested the widget with different screen readers (JAWS / NVDA / VoiceOver), it displayed different behaviors!

It took me time and effort to realize that ARIA 1.1 proposes some significant changes to how combobox elements should be organized, which many screen readers don’t support.

ARIA Wiki documents the history of encountering issues from 1.0, proposing a fix in 1.1, which caused more problems, and 1.2 reverted back to 1.0 with minor differences.

How can we tell if a combobox is ARIA v1.1 or v1.2?

Check two things:

  • role="combobox" is on a <div>(v.1.1) or <input>(v.1.2)
  • aria-owns (v.1.1) vs. aria-controls (v1.2)

With this knowledge, you can inspect the element and quickly tell which version it is.

Many libraries make the same mistake to implement the ARIA 1.1 combobox. Some examples include Deque Labs, Downshift.

The X Factor: screen readers

Another reason why it was difficult to implement accessible comboboxes was because of the testing effort.

For each change, we need to test in the following combo

  • VoiceOver + Safari + Mac OS X
  • JAWS + Chrome + Windows
  • NVDA + Firefox + Windows

Generally, there could be bugs with screen reader software itself. Some versions of NVDA + Firefox combo may break. So when testing, it took efforts to figure out it’s my implementation, or the dependencies.

We usually we test on latest versions of all dependencies. But sometimes our accessibility auditors forgot to update their software, tests failed and we couldn’t reproduce because we use a different versions.

To make things worse, screen readers implementations could vary in subtle ways.

Only NVDA is open source. But JAWS and VoiceOver are both proprietary, so a lot of behaviors are not 100% specified, esp. involving the virtual cursors. We had to test it on all environments to make sure it covers all a11y issues. Generally VoiceOver is substantially different from the other two.

Finally, screen reader virtual cursors are tricky to work with. You can read about this topic in another post.

Any silver lining?

Yes! I open source my combobox implementation in a Github repo.

It is based on ARIA 1.1 reference implementation with v1.2 fixes.

The implementation also comes with extra things like:

  • Initial value
  • More callback hooks to make it to extend and implement other behaviors
  • ES6 class syntax

Check out the code at Github.

What about existing accessible UI libraries?

I checked a few libraries, including:

Only Reach UI implements the ARIA 1.2 combobox. Its API makes a lot of sense and the documentation is also quite helpful.

The only thing missing for Reach UI is a button that can trigger the popups. Some people think that the button isn’t the essential to the combobox. But with some work, it shouldn’t be hard to add the button.

If you are looking for an accessible combobox in React, Reach UI is definitely the one to go with.

Deque Labs and Downshift are still following ARIA 1.1, which should be updated to 1.2 to avoid screen reader support problems.

For Adobe and Reakit, I couldn’t find any combobox implementations.


Implementing an accessible combobox from scratch is not easy. I had to jump through the hoops about outdated ARIA specs, resisting the urge to misuse HTML semantics, and spending lots of testing effort.

To help future readers to avoid the mistakes I made, I wrote this post and also put my implementation in Github.

If you are using React, I recommend Reach UI Combobox, which implements ARIA 1.2 combobox correctly and straightforward to use. Using a third-party libraries to deal with all accessibility issues is really nice.

Happy coding!