Open junjizhi opened 4 years ago
TBA
https://www.w3.org/TR/wai-aria-practices/#menubutton https://www.w3.org/TR/wai-aria-practices/#combobox
The element with role button has aria-haspopup set to either menu or true.
So the aria-hashpopup
should be set to what value?
A: combobox
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.
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 combobox consists of a few parts:
The ARIA site has detailed specs about:
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.
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:
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:
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:
<div>
to wrap any components without caring about its semantic.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.
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.
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
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.
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:
Check out the code at Github.
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 easy is not easy. I had to jump through the hoops about outdated ARIA specs, resisting the urge to abuse HTML semantics, and spending lots of testing effort. Future readers could avoid this by reusing the lessons learned.
Also, I put my implementation in Github in case anyone wants a reference.
If you are using React, I recommend Reach UI Combobox, which implements ARIA 1.2 combobox correctly and easy to use. Using a third-party libraries to deal with all accessibility issues is really nice.
Happy coding!
https://dequelabs.github.io/combobo/demo/
It uses aria-owns
instead of aria-controls
which means it is not ARIA v1.2 compliant.
https://www.npmjs.com/package/downshift
From the generated code, it wraps <input>
in a <div>
which has the role="combobox"
. So it is still following the ARIA v1.1 implementation which would lack of ARIA v1.2 compliance, and many screen readers won't support this well.
I check Adobe UI library: https://react-spectrum.adobe.com/react-spectrum/styling.html
It doesn't come with a combobox guidelines or component implementation.
https://reakit.io/docs/button/
I didn't find any combobox implementation either.
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)
W3 Standard doesn't cover this case, so I can only infer the most appropriate way of implementing this.