Who is using one
Screen reader users, people who cannot use a mouse, people using voice control or a switch, people whose trackpad has died — and every developer who has ever pressed Tab. If something can be clicked, it must be reachable and operable without a pointer. That is one WCAG criterion, and it is the one that fails most often.
The keys, and what owes what
The split matters: Tab moves between widgets, arrows move inside one. A group of radio buttons is one Tab stop, and the arrows choose. A set of tabs is one Tab stop. Getting that wrong means forty Tab presses to cross a toolbar.
| Key | Does |
|---|---|
| Tab / Shift+Tab | move between focusable things, in order |
| Enter | activate a link or a button; submit a form |
| Space | activate a button; check a checkbox; scroll the page |
| Arrows | move within a widget: radio group, tabs, menu, select |
| Escape | close a dialog, a menu, a popover — without saving |
| Home / End | first and last, inside a widget |
The tab order is the DOM order
order, row-reverse, grid-area and absolute positioning can all reorder what you see without reordering what you Tab through. Keep the DOM in the reading order and use CSS for appearance, not for sequence.
<div style="display: flex; flex-direction: row-reverse">
<button>First in the DOM, last on the screen</button>
<button>Second in the DOM, first on the screen</button>
</div>
<!-- Tab goes in DOM order. The eye goes in visual order. They must agree. -->
tabindex: three values, two of them fine
A positive tabindex puts an element before everything with tabindex 0 — including elements earlier on the page — and one on a page is enough to make the whole order unpredictable. tabindex="-1" is the useful one: it is how you move focus to a heading after a route change, or to the first error in a form.
<div tabindex="0"> <!-- add to the natural order, where it sits -->
<div tabindex="-1"> <!-- not in the order, but focusable from script: focus() -->
<div tabindex="5"> <!-- jump the queue. do not. -->
It makes it focusable. It has no role, no name and no key handling. Either write all three, or — as the semantics lesson said — use a button.
Traps
A keyboard trap is anything you can Tab into and not out of: a third-party embed that swallows Tab, a custom editor, a modal that loops focus but has no Escape. It is a level-A failure because it ends the session — the person has to close the tab.
A modal dialog is the one place where trapping focus is correct, and it must come with Escape and a return of focus to whatever opened it. That is the Dialogs lesson.
Shortcuts
A single-letter shortcut steals that letter from voice-control users and from anyone typing in a field your handler did not exclude. If you offer them, require a modifier, or let them be remapped or turned off — which is a WCAG 2.1 criterion in itself.