Focusable elements need a role that fits
- Rule:
axe/focus-order-semantics - Checks: Elements in the focus order should have an appropriate role
- WCAG: none; a best practice, not a WCAG success criterion
- Fix class:
mechanical: A fix needs no authored text or visual decision.
Who it affects
Section titled “Who it affects”- People who use a screen reader or a braille display, most of them blind.
- People who use a keyboard or a switch device instead of a mouse.
How to fix it
Section titled “How to fix it”A <div> or <span> that acts as a control has tabindex="0"
Section titled “A <div> or <span> that acts as a control has tabindex="0"”Use a native <button>, or <a href> if it navigates, which brings focus, keyboard activation and the role with it. If the element must stay custom, give it the matching role, such as role="button", and handle Enter and Space.
<button type="button" class="filter-toggle">Show filters</button>A heading or paragraph has tabindex="0" so script can move focus to it
Section titled “A heading or paragraph has tabindex="0" so script can move focus to it”Use tabindex="-1". Script can still focus it, such as after a route change, without adding a stop to the tab order.
<h1 tabindex="-1">Search results</h1>A scrollable <div> has tabindex="0" so keyboard users can scroll it
Section titled “A scrollable <div> has tabindex="0" so keyboard users can scroll it”Keep the tabindex, and give it role="region" and a name, with aria-label or aria-labelledby, that says what it holds.
<div class="log" tabindex="0" role="region" aria-label="Build log"> <pre>Build finished in 12 seconds</pre></div>Fixes that fail
Section titled “Fixes that fail”Adding role="button" without keyboard handling
Section titled “Adding role="button" without keyboard handling”Screen readers announce a button, but Enter and Space do nothing until script handles them, which a native <button> does for free.
Removing tabindex from an element people need to operate
Section titled “Removing tabindex from an element people need to operate”The rule passes, but keyboard users can no longer reach the control at all.
In React
Section titled “In React”The part of the fix that differs in React, in its syntax. The rest of the guidance holds.
An effect focuses the page heading after a route change, so the heading has tabIndex={0}
Section titled “An effect focuses the page heading after a route change, so the heading has tabIndex={0}”Use tabIndex={-1}. Calling focus() on the ref still works, and the heading adds no stop to the tab order.
function PageHeading({ children }) { const ref = useRef(null);
useEffect(() => { ref.current.focus(); }, []);
return ( <h1 ref={ref} tabIndex={-1}> {children} </h1> );}Fixes that fail in React
Section titled “Fixes that fail in React”Adding tabIndex={0} and onKeyDown to a clickable <div> to satisfy jsx-a11y
Section titled “Adding tabIndex={0} and onKeyDown to a clickable <div> to satisfy jsx-a11y”That quiets click-events-have-key-events, but the element still has no role, so no-static-element-interactions and this rule still fail and screen readers announce no control. Render a <button type="button"> and give it the onClick.
More on the rule
Section titled “More on the rule”axe-core’s documentation of the rule
CORA’s fix guidance adapts content from accessibility-agents, under the MIT License: see its notice.