CANON · Keyboard
If it doesn't work with a keyboard, it doesn't work. Period.
Tab order = visual order
Tab moves focus through interactive elements in document order. That order must match the visual layout. If CSS rearranges elements visually (flexbox order, grid placement, position: absolute), the tab order still follows DOM order. Fix the DOM, not the visual.
Never use tabindex > 0. It forces elements to the front of tab order globally and breaks the natural flow.
| tabindex value |
Meaning |
| 0 |
Focusable in natural order |
| -1 |
Focusable only via JS (.focus()), not via Tab |
| > 0 |
Never use. Forces unnatural order. |
Interactive elements are natively focusable
<button>, <a href>, <input>, <select>, <textarea> — all keyboard-accessible by default. If you're adding tabindex="0" and onKeyDown to a <div>, you've reinvented a button badly.
Composite widgets — arrow keys, not Tab
Inside a toolbar, tab bar, radio group, or menu, arrow keys move between items. Tab enters and exits the group. This is the roving tabindex or aria-activedescendant pattern.
| Widget |
Internal navigation |
| Tabs |
Arrow Left/Right (horizontal), Up/Down (vertical) |
| Menu |
Arrow Up/Down, Enter to activate, Escape to close |
| Toolbar |
Arrow Left/Right between buttons |
| Radio group |
Arrow Up/Down or Left/Right |
| Tree view |
Arrow Up/Down to traverse, Right to expand, Left to collapse |
Keyboard shortcuts
- Document them. An undiscoverable shortcut is a non-shortcut.
- Use
⌘/Ctrl + letter for common actions (⌘S save, ⌘K command palette).
- Don't override browser shortcuts (⌘L address bar, ⌘T new tab, ⌘W close tab).
- Use single-key shortcuts (like
? for help) only in apps, not content pages (WCAG 2.1.4).
- Allow users to remap or disable shortcuts.
Focus indicators
See canon-focus for visual specs. Key rule: every focused element must be visually distinguishable. outline: none without a replacement is an accessibility violation.
Anti-patterns
| Anti-pattern |
Why it fails |
tabindex > 0 |
Breaks global tab order |
<div onClick> without keyboard handler |
Keyboard users blocked |
| Overriding ⌘W, ⌘T, ⌘L |
Hijacking browser |
| No skip link |
Users Tab through entire nav on every page |
| Tab trapping outside of modals |
Users stuck |
| Undocumented keyboard shortcuts |
Undiscoverable |
Audit checklist
Sources
- WCAG 2.2 · 2.1.1 Keyboard, 2.1.2 No Keyboard Trap, 2.1.4 Character Key Shortcuts, 2.4.3 Focus Order
- WAI-ARIA Authoring Practices · Keyboard interaction patterns
- Apple HIG · Keyboard support
1---2name: canon-keyboard3description: Use when designing or auditing keyboard navigation, focus management, shortcut keys, or keyboard-only workflows. Covers tab order, arrow keys within composite widgets, skip links, shortcut conventions, and ensuring every interaction works without a mouse. Trigger when the user mentions keyboard, tab order, shortcuts, hotkeys, focus order, or keyboard navigation.4---56# CANON · Keyboard78If it doesn't work with a keyboard, it doesn't work. Period.910## Tab order = visual order1112Tab moves focus through interactive elements in document order. That order must match the visual layout. If CSS rearranges elements visually (flexbox `order`, grid placement, `position: absolute`), the tab order still follows DOM order. Fix the DOM, not the visual.1314Never use `tabindex` > 0. It forces elements to the front of tab order globally and breaks the natural flow.1516| tabindex value | Meaning |17|---|---|18| 0 | Focusable in natural order |19| -1 | Focusable only via JS (`.focus()`), not via Tab |20| > 0 | **Never use.** Forces unnatural order. |2122## Interactive elements are natively focusable2324`<button>`, `<a href>`, `<input>`, `<select>`, `<textarea>` — all keyboard-accessible by default. If you're adding `tabindex="0"` and `onKeyDown` to a `<div>`, you've reinvented a button badly.2526## Composite widgets — arrow keys, not Tab2728Inside a toolbar, tab bar, radio group, or menu, arrow keys move between items. Tab enters and exits the group. This is the **roving tabindex** or **aria-activedescendant** pattern.2930| Widget | Internal navigation |31|---|---|32| Tabs | Arrow Left/Right (horizontal), Up/Down (vertical) |33| Menu | Arrow Up/Down, Enter to activate, Escape to close |34| Toolbar | Arrow Left/Right between buttons |35| Radio group | Arrow Up/Down or Left/Right |36| Tree view | Arrow Up/Down to traverse, Right to expand, Left to collapse |3738## Keyboard shortcuts3940- Document them. An undiscoverable shortcut is a non-shortcut.41- Use `⌘/Ctrl + letter` for common actions (⌘S save, ⌘K command palette).42- Don't override browser shortcuts (⌘L address bar, ⌘T new tab, ⌘W close tab).43- Use single-key shortcuts (like `?` for help) only in apps, not content pages (WCAG 2.1.4).44- Allow users to remap or disable shortcuts.4546## Focus indicators4748See `canon-focus` for visual specs. Key rule: every focused element must be visually distinguishable. `outline: none` without a replacement is an accessibility violation.4950## Anti-patterns5152| Anti-pattern | Why it fails |53|---|---|54| `tabindex` > 0 | Breaks global tab order |55| `<div onClick>` without keyboard handler | Keyboard users blocked |56| Overriding ⌘W, ⌘T, ⌘L | Hijacking browser |57| No skip link | Users Tab through entire nav on every page |58| Tab trapping outside of modals | Users stuck |59| Undocumented keyboard shortcuts | Undiscoverable |6061## Audit checklist6263- [ ] Every interactive element reachable via Tab (or arrow keys within composites)64- [ ] Tab order matches visual order65- [ ] No `tabindex` > 066- [ ] Custom controls use `<button>` or proper ARIA + keyboard handlers67- [ ] Skip link on every page68- [ ] Keyboard shortcuts documented and non-conflicting with browser69- [ ] Modal focus traps work correctly (Tab cycles, Escape exits)70- [ ] Focus visible on every focused element7172## Sources7374- WCAG 2.2 · 2.1.1 Keyboard, 2.1.2 No Keyboard Trap, 2.1.4 Character Key Shortcuts, 2.4.3 Focus Order75- WAI-ARIA Authoring Practices · Keyboard interaction patterns76- Apple HIG · Keyboard support