widget-keyboard-focusable

Widgets should be reachable with the keyboard

manualWCAG 2.1.1 (A)confidence: mediumseverity: seriousoperable

Flags an element with a widget role that cannot take focus or is out of the tab order, and a composite widget with no tab stop, for manual review.

Applies to. Elements the page shows with aria-activedescendant, and those whose role, from the role attribute or a custom element's ElementInternals, is a widget a user operates:

  • single widgets: button, link, checkbox, switch, radio, slider, spinbutton, textbox, searchbox, combobox;
  • composite widgets: grid, treegrid, listbox, menu, menubar, tree, tablist, toolbar, radiogroup.

Left out:

  • an element out of the accessibility tree (aria-hidden="true"), or disabled (disabled, or aria-disabled="true" on it or an ancestor): it offers nothing to operate;
  • a widget inside a composite widget, or inside an element that can take focus and whose aria-activedescendant points at an item (by id, or set from script as ariaActiveDescendantElement; an empty attribute points at nothing): the composite moves focus among its items (a roving tabindex) or keeps it and points at the active one, so the outer element is judged instead (for a submenu, its menubar);
  • an element with an inline onclick, or a click listener the listener hook (listenerHookScript) saw, which click-handler-keyboard-operable judges.

Expectation. A keyboard user can reach the widget with Tab (WCAG 2.1.1). Only the markup's focusability is read: a script may move focus or handle keys in ways the markup doesn't show, so a finding is a question, never a failure.

  • A single widget that is in the tab order, or holds an element that is, passes.
  • A single widget that can't take focus, and holds nothing that can: cantTell (WIDGET_NOT_FOCUSABLE). Check that a keyboard user can reach it, or what it does, another way.
  • A single widget that takes focus only from script (tabindex="-1"): cantTell (WIDGET_NOT_IN_TAB_ORDER). Tab never reaches it; check that a script moves focus to it, or that what it does is offered elsewhere.
  • A composite widget passes when it is in the tab order (with aria-activedescendant pointing at its active item, or not) or holds an element that is. One that no other element names in aria-controls or aria-owns (a popup opened by a button or combobox gets focus from it) and that has neither: cantTell (COMPOSITE_NO_TAB_STOP).
  • An element of any role carrying aria-activedescendant that can't take focus: cantTell (ACTIVEDESCENDANT_HOST_NOT_FOCUSABLE). The active item is announced only while that element has focus. Such an element doesn't manage focus for the widgets inside it, so they are judged on their own.

What a finding reports.

  • role: the element's role.
  • focusable: whether the element itself can take focus.
  • scriptFocusable: for a composite, whether any element in it takes focus from script (tabindex="-1").

These data.details fields help to read and reproduce a finding. Apart from reasonCode and the few fields Output schema names as stable, they are not a stable contract: a field may be renamed or dropped in a minor release, so don’t build on them.

Examples

pass
<div role="button" tabindex="0">Save</div>

The custom button is in the tab order.

can't tell
<div role="button">Save</div>

The element says it is a button, but Tab never reaches it. A person checks that a keyboard user can reach it, or what it does, another way.

How to check

When this rule can’t decide, it reports cantTell and leaves the question to a person. Each question it can ask is listed here under its reason code, with the steps to answer it. A finding carries the same steps as howToCheck, in the scan’s language. See Output schema.

ACTIVEDESCENDANT_HOST_NOT_FOCUSABLE

  1. Find the element the report names; it uses aria-activedescendant to point at its active item.
  2. Using only the keyboard, open or reach the widget (for example type in its search box) and watch whether that element, not something else, gets focus.
  3. Press the arrow keys with a screen reader running and listen whether each active item is announced.
  4. If the items cannot be reached this way, check whether the same choices can be made another way with the keyboard alone, for example by typing the value into a text field. It only counts if every choice is available that way and a screen reader announces it.

Pass. The element gets focus and the screen reader announces each active item as the arrow keys move, or the same choices can be made another way with the keyboard alone and the screen reader announces them.

Fail. The element cannot get focus, so moving through items announces nothing or the items cannot be reached, and the choices cannot be made another way with the keyboard, which fails WCAG 2.1.1.

COMPOSITE_NO_TAB_STOP

  1. Find the widget the report names (a menu, list box, tab list, toolbar, grid or tree).
  2. Click just before it and press Tab, and watch whether focus enters it or one of its items.
  3. If it is a popup, open it from its button with the keyboard and see whether focus moves into it.
  4. Once inside, check that the arrow keys move between the items.
  5. If focus never gets in, check whether what the widget does can be done another way with the keyboard alone, for example the same choices offered as links or buttons elsewhere on the page. It only counts if every option the widget offers is available that way.

Pass. Tab (or the button that opens it) puts focus in the widget and the arrow keys move between its items, or everything its items do can be done another way with the keyboard alone.

Fail. Focus never gets into the widget or its items with the keyboard, and what they do cannot be done another way with the keyboard, which fails WCAG 2.1.1.

WIDGET_NOT_FOCUSABLE

  1. Find the widget the report names and note what it does when clicked or touched.
  2. Click just before it, then press Tab and watch whether focus ever lands on it.
  3. If it never gets focus, look for another control a keyboard can reach that does the same thing.
  4. If the element does nothing, its role may be wrong; note that instead.

Pass. The widget gets keyboard focus and works with the keyboard, or the same function is offered by another keyboard-reachable control.

Fail. The widget cannot be reached or used with the keyboard and its function is not offered elsewhere, which fails WCAG 2.1.1 (F42).

WIDGET_NOT_IN_TAB_ORDER

  1. Find the widget the report names and note when it appears (for example when a menu or dialog opens).
  2. Use only the keyboard to open or reach that part of the page, and watch whether a script moves focus to the widget, or the arrow keys reach it.
  3. If focus never reaches it, look for another keyboard-reachable control that does the same thing.

Pass. Focus is moved to the widget by script or by arrow keys when needed, or its function is offered elsewhere.

Fail. Tab and arrow keys never reach the widget and nothing else offers its function, which fails WCAG 2.1.1.

View source