Sorry, we don't support your browser.  Install a modern browser

Improve accessibility of dropdown and multiselect field

I’ve compiled these notes mainly from the WAI-ARIA’s documentation and by observing OS’s native ui elements. I’ll try to add sources later, but as a starting point, WAI-ARIA’s Authoring Practices documentation has a lot of information as well as many implementation examples of listbox and combobox elements.

If the select field is open…

  • <kbd>Up</kbd> / <kbd>Down</kbd> should obviously highlight the next/previous option. If the first/last option is selected, it does nothing.
  • <kbd>Meta</kbd> + <kbd>Up</kbd> / <kbd>Meta</kbd> + <kbd>Down</kbd> should highlight the first/last option.
  • Typing any character will highlight the next option that starts with the given key. If no option matches, nothing happens.
  • Typing multiple characters in quick succession will highlight the next option that starts with that key sequence. If no option matches, the next option that starts with the last key pressed is highlighted, else nothing happens.
  • If an option is highlighted, that isn’t fully visible (because the list of options is scrollable), it should be scrolled to the top or button of the container.
  • <kbd>Enter</kbd> / <kbd>Space</kbd> toggles the currently highlighted option.
  • <kbd>Shift</kbd> + <kbd>Up</kbd> / <kbd>Shift</kbd> + <kbd>Down</kbd> highlights and selects the next/previous option.
  • <kbd>Shift</kbd> + <kbd>Meta</kbd> + <kbd>Up</kbd> / <kbd>Shift</kbd> + <kbd>Meta</kbd> + <kbd>Down</kbd> highlights the first/last option and selects all options from the previously highlighted option throughout the the first/last option.
  • <kbd>Tab</kbd>Tab moves the focus out of the field, not to the next option.
  • <kbd>Esc</kbd> closes the field and keeps focus without changing the status of the highlighted option.
  • Moving focus out of the field, e. g. using the <kbd>Tab</kbd> key, closes the field without changing the status of the highlighted option.

If the select field is closed…

  • <kbd>Up</kbd> / <kbd>Down</kbd>, <kbd>Space</kbd>, <kbd>Enter</kbd>, open the field. The first selected option is highlighted, or – if none are selected – the first option.

For screen reader support, it’s worked well for me to use role="listbox" and role="option". In practice, you will also need a button element to toggle the visibility of the listbox. I’ve tested this solution in macOS’s VoiceOver, briefly in NVDA on Windows and wasn’t test on a machine with JAWS installed, so treat the following with caution…

<div class="multiselect">
  <div
    class="multiselect-button"
    role="button"
    aria-haspopup="listbox"

    <!-- Using `tabindex="0"` makes the element focusable without changing
         the natural tab order
    -->
    tabindex="0"

    <!-- While in HTML5, boolean attributes may be defined without an explicit
         this is not the case for `aria-*` attributes.
    -->
    aria-expanded="true"

    <!-- This explicitly sets the label that is announced by screen readers
         onfocus. It includes the currently selected option. Without this, different
         screen readers seem to interpret the ARIA roles and attributes differently
         which leads to confusing descriptions of the element.
    -->
    aria-label="Select an option: Option B"
  >
    Option B
  </div>
  <ul
    class="multiselect-options"
    role="listbox"
    aria-multiselectable="true"
  >
    <!-- Using `tabindex="-1"` makes the element focusable programmatically (but not by tabbing). -->
    <li class="multiselect-option" tabindex="-1" id="option-a" role="option" aria-selected="false">
      Option A
    </li>
    <li class="multiselect-option" tabindex="-1" id="option-b" role="option" aria-selected="true">
      Option B
    </li>
    <li class="multiselect-option" tabindex="-1" id="option-c" role="option" aria-selected="false">
      Option C
    </li>
  </ul>
</div>

This way, the field (actually the toggle button) is focussable by using the tab key. Highlighting an option should programmatically move the focus to the respective option element. Alternatively, you may also choose to keep the focus on the listbox element and add appropriate styles to the highlighted option. Then you’ll need to add an aria-activedescendant="option-b" attribute to the listbox element, to inform screen readers of the highlighted element. After closing the listbox, focus should be set back on the toggle button.

5 years ago