Standard HTML elements such as
<input> have keyboard accessibility built in for free. If you're building custom interactive components, however, use the
tabindex attribute to ensure that they're keyboard accessible.
Check if your controls are keyboard accessible #
A tool like Lighthouse is great at detecting certain accessibility issues, but some things can only be tested by a human.
Try pressing the
Tab key to navigate through your site. Are you able to reach all the interactive controls on the page? If not, you may need to use
tabindex to improve the focusability of those controls.
Insert an element into the tab order #
Insert an element into the natural tab order using
tabindex="0". For example:
<div tabindex="0">Focus me with the TAB key</div>
To focus an element, press the
Tab key or call the element's
Remove an element from the tab order #
Remove an element using
tabindex="-1". For example:
<button tabindex="-1">Can't reach me with the TAB key!</button>
This removes an element from the natural tab order, but the element can still be focused by calling its
Note that applying
tabindex="-1" to an element doesn't affect its children; if they're in the tab order naturally or because of a
tabindex value, they'll remain in the tab order. To remove an element and all its children from the tab order, consider using the WICG's
inert polyfill. The polyfill emulates the behavior of a proposed
inert attribute, which prevents elements from being selected or read by assistive technologies.
tabindex > 0 #
tabindex greater than 0 jumps the element to the front of the natural tab order. If there are multiple elements with a
tabindex greater than 0, the tab order starts from the lowest value greater than zero and works its way up.
tabindex greater than 0 is considered an anti-pattern because screen readers navigate the page in DOM order, not tab order. If you need an element to come sooner in the tab order, it should be moved to an earlier spot in the DOM.
Lighthouse makes it easy to identify elements with a
tabindex > 0. Run the Accessibility Audit (Lighthouse > Options > Accessibility) and look for the results of the "No element has a [tabindex] value greater than 0" audit.
Create accessible components with "roving
If you're building a complex component, you may need to add additional keyboard support beyond focus. Consider the built-in
select element. It is focusable and you can use the arrow keys to expose additional functionality (the selectable options).
To implement similar functionality in your own components, use a technique known as "roving
tabindex". Roving tabindex works by setting
tabindex to -1 for all children except the currently-active one. The component then uses a keyboard event listener to determine which key the user has pressed.
When this happens, the component sets the previously focused child's
tabindex to -1, sets the to-be-focused child's
tabindex to 0, and calls the
focus() method on it.
This HTML renders a modal dialog:
<div role="dialog" aria-labelledby="dialog-header">
Do you want to allow notifications from this website?
What is the tab order for the elements in the sample?
- The Close button
- The No button
- The Yes button
<button> elements are included in the tab order because they're the only standardized HTML form elements. To insert other elements into the tab order, you would add a
<li>A group of cats is called a <a href="https://m-w.com/dictionary/clowder">clowder</a>.</li>
<li>Most cats are <a href="https://www.catfacts.org/catnip.html"> unaffected by catnip</a>.</li>
Which elements from the sample are included in the tab order?
<a> elements are included in the tab order.
<section> element is not in the tab order because it has a negative
tabindex value. (It can, however, be focused using the
focus() method.) The
tabindex value for the
<section> element doesn't affect its children.
This HTML renders a popup menu followed by a search input:
<div role="menu" tabindex="0">
<a role="menuitem" href="/learn/" tabindex="-1">Learn</a>
<a role="menuitem" href="/measure/" tabindex="-1">Measure</a>
<a role="menuitem" href="/blog/" tabindex="-1">Blog</a>
<a role="menuitem" href="/about/" tabindex="-1">About</a>
<input tabindex="1" type="text" role="search" aria-label="Search" placeholder="Search">
Which element in the sample comes first in the tab order?
The Search text input comes first in the tab order. Because it has a
tabindex greater than zero, it jumps to the front of the tab order.
(This behavior is likely to cause confusion if the menu is positioned on the page before the search input. This is an example of why having a
tabindex value greater than zero is considered an anti-pattern.)
<div role="radiogroup" aria-labelledby="breed-header">
<h3 id="breed-header">Your cat's breed</h3>
<div role="radio" aria-checked="false" tabindex="0">Persian</div>
<div role="radio" aria-checked="false" tabindex="-1">Bengal</div>
<div role="radio" aria-checked="false" tabindex="-1">Maine Coon</div>
role="radio" element is focused, what should happen when a user presses the
Right arrow key?
- Change the
tabindexvalues for all radio elements in the group to -1.
- If there's a radio element after the one that's focused, set its
tabindexvalue to 0.
- If there's no radio element after the one that's focused, set the
tabindexvalue of the first radio element in the group to 0.
- Focus the radio element that now has a
That's a lot—and it doesn't even include ARIA attributes! This is an example of why it's easier to use built-in elements with built-in keyboard behavior whenever you can.
Keyboard access recipes #
If you're unsure what level of keyboard support your custom components might need, you can refer to the ARIA Authoring Practices 1.1. This handy guide lists common UI patterns and identifies which keys your components should support.