Listbox Select Widget Implementation Examples

This widget is based on the Listbox with Grouped Options and the Actions Menu Button examples from the WAI ARIA Authoring Practices 1.2. It emulates most of the behaviors of an HTML select element with optgroup containers.

Some of the “groups” in the first instance of this control on the page are collapsable. Other groups are static / non-collapsable. We attempt to ensure a correct count of options via aria-posinset and aria-setsize and make group associations for options via aria-describedby.

Typically, options should be static and selectable (unless disabled), but some of the options marking the group names in this implementation are toggles that expand the group. In a previous iteration, expandable groups were triggered by buttons (with a button role) using aria-expanded to report state. Because JAWS will always toggle to Virtual PC Cursor/browse mode when a button is focused, even when the button is in a composite widget (in this case, in a listbox), we were forced to use role="option" on the buttons. In addition to triggering a 4.1.2 violation in axe, this will cause some screen readers to not report the expanded/collapsed state of the buttons, since aria-expanded is not supported on elements with role="option". An ugly way around this would be to shim in some sort of aria-label or non-visible accessible text in order “fake” the state (🤮). In both instances on this page, we use aria-selected to register the selected state of options.

Unlike a native select, selection only occurs via keyboard Spacebar or Enter or by clicking, not solely by roving the focus via arrow keys. This allows a user to navigate the options list without having to make a selection. Escape collapses the listbox and re-focuses the trigger aria-haspopup button, which always displays the current selection. A click or focus outside of the widget will also close it.

Listbox Select Implementation with Expandable Options (BAD)

Animals
water buffalo
ibex
ocelot
puma
mountain lion
eagle
vulture
pigeon
dogs
malamute
pit bull
coyote
goat
marmoset
velociraptor
brontosaurus
tyrannosaurus rex
skunk

A link that doesn't go anywhere, after the widget, as an anchor to test tabbing through or out of the widget.

Listbox Select Implementation with Staticly Open Options (GOOD)

Animals
water buffalo
ibex
cats
ocelot
puma
mountain lion
birds
eagle
vulture
pigeon
dogs
malamute
pit bull
coyote
goat
marmoset
dinosaurs
velociraptor
brontosaurus
tyrannosaurus rex
skunk

Another link that doesn't go anywhere, after the widget, an anchor to test tabbing through or out of the widget.

Testing Results and Commentary

Environment: Testing conducted May, 2022. All technologies evaluated at most recently released versions as of testing date.

Summary: The BAD example is a kludge, and it shows in testing. Cardinality is confusing, as it is necessary to add the collapsible option elements into the total list count — thereby giving a confusing total number of options. In addition, not all screen readers are announcing the expanded/collapsed state of the expandable options, causing potential problems understanding one's position/orientation within the list. For iOS + VoiceOver, where semantics are minimal to begin with, the non-announcement of expanded/collapsed state of options would likely make the widget highly prone to causing incorrect entry or, minimally, user frustration.

The GOOD example is acceptable. Group affiliations and cardinality (when reported) are accurate and clear. The outlier is iOS + VoiceOver, which fails to report list boundaries or selected state. To make the widget boundaries clear, we could shim in visually hidden text announcing the beginning and end of the list. However, that may cause technical debt in the future, since the text for that announcement would need to be consistent across all uses of the component (difficult to guarantee) and would need to be internationalized.

Note: If this widget were used along with a dynamic search, an appropriate aria-live announcement of number of results returned would be something like “X items, in Y groups.”

For the select with collapsible groups, better structures than a listbox with options are a tree or a menu. We have both an example of a tree-based select and an example of a menu-based select available, with test results and commentary.