The ground rules
- Everything reachable: every function works with a keyboard (2.1.1 Keyboard). Tab and Shift+Tab move between controls, Enter activates links and buttons, Space activates buttons and checkboxes.
- No traps: you can always move away from a component with the keyboard (2.1.2 No Keyboard Trap).
- Logical order: focus follows the meaning of the page (2.4.3 Focus Order).
- Always visible: you can see where focus is (2.4.7 Focus Visible), and it is not hidden (2.4.11 Focus Not Obscured (Minimum)).
- Shortcuts past repetition: a “Skip to content” link bypasses the navigation (2.4.1 Bypass Blocks).
Real buttons, not clickable divs
A <button> is focusable out of the box, responds to Enter and Space and is announced as a button. A <div> with a click handler does none of that. Use <button> for actions and <a href> for navigation. Less code, fewer bugs.
<div class="btn" onclick="save()">
Save
</div><button type="button" class="btn">
Save
</button>The button is focusable, works with Enter and Space and has the right role.
tabindex: 0 and -1 yes, positive values no
tabindex="0" adds an element to the natural tab order, for example a custom widget. tabindex="-1" makes an element focusable by script without putting it in the tab order, for example a heading you move focus to after a route change. Positive values such as tabindex="3" scramble the order and are almost never the right fix. Arrange the HTML in the order it should be read instead.
Route changes in single-page apps
On a traditional website a click loads a new page and the screen reader starts at the top. In a single-page app, JavaScript swaps the content. Focus stays on the link that was clicked, which may no longer exist, and nobody is told that anything changed.
A proven approach: after each route change, update document.title (2.4.2 Page Titled) and move focus to the new main heading. Alternatively, announce the change through a live region, as shown in our pattern status message.
router.on('change', view => {
main.innerHTML = view.html;
});router.on('change', view => {
main.innerHTML = view.html;
document.title = view.title;
const h1 = main.querySelector('h1');
h1.setAttribute('tabindex', '-1');
h1.focus();
});Dialogs, menus and returning focus
When a modal dialog opens, focus moves into it. While it is open, Tab stays inside, Escape closes it, and afterwards focus returns to the element that opened it. The native <dialog> element with showModal() handles much of this for you. See it in action in our pattern modal dialog, and for menus in disclosure navigation.
Note: trapping focus is right only for genuinely modal dialogs. Anywhere else it is a keyboard trap.
Custom shortcuts, used with care
Single-character shortcuts, such as “s” for search, fire by accident with voice control. 2.1.4 Character Key Shortcuts requires that such shortcuts can be turned off or remapped, or only work while the relevant component has focus. Shortcuts with Ctrl or Alt are not affected.
Checklist
0 of 8 done
Your ticks stay in this browser tab only and are not stored anywhere.
Related WCAG criteria
- 2.1.1 Keyboard
- 2.1.2 No Keyboard Trap
- 2.1.4 Character Key Shortcuts
- 2.4.1 Bypass Blocks
- 2.4.3 Focus Order
- 2.4.7 Focus Visible
- 2.4.11 Focus Not Obscured (Minimum)
- 2.4.2 Page Titled
Tools and patterns for this
- Keyboard testSee and practise focus
- Modal dialogFocus moves in on open, Escape closes, focus returns.
- Navigation with submenus that does not need a mouseSubmenus behind buttons, Escape closes, plain links.
- Status messages that get through without stealing focusAnnounce messages via a live region, focus stays put.
- Free checkTest your home page automatically in two minutes
Frequently asked questions
What is the quickest keyboard test?
Put the mouse aside and go through one key journey using Tab, Shift+Tab, Enter, Space and Escape. Watch whether focus is always visible and whether you can always get out again.
Do custom widgets need arrow keys?
Composite widgets such as tabs, menus or listboxes are expected to behave as described in the WAI-ARIA Authoring Practices Guide, usually with arrow keys. Simple buttons and links do not need them.
What about hover menus?
Anything that appears only on mouse hover is often out of reach for keyboard and touch users. Open sub-menus with a button that carries aria-expanded.
Sources
How does your website do?
The free check tests your home page in two minutes. The audit decides all 55 criteria, by a person.