A keyboard navigation checklist
Unplug your mouse for ten minutes. You will find more bugs than any automated scanner.
The cheapest accessibility test
Automated accessibility checkers catch missing alt text and low contrast, but they cannot tell you whether your interface is actually usable without a mouse. For that, you need to put the mouse away and try. Ten minutes with only the keyboard on your main flows will reveal more real problems than a long automated report.
The checklist
Can you reach everything?
Press Tab repeatedly from the top of the page. Every link, button, form field and custom control should receive focus in turn. Anything you can click but cannot tab to is a bug. The usual cause is a clickable div that should have been a button.
Can you see where you are?
Focus must be visible at all times. Many sites remove the default outline for aesthetic reasons and never replace it. Use :focus-visible to show a clear ring for keyboard users without showing it on mouse clicks.
Is the order sensible?
Focus should move in the same order as the visual reading order: left to right, top to bottom in most layouts. Positive tabindex values and CSS that reorders content visually are common causes of focus jumping around unpredictably.
Can you operate every control?
- Buttons activate with Enter and Space.
- Links activate with Enter.
- Checkboxes toggle with Space.
- Menus, tabs and listboxes move with arrow keys.
- Escape closes dialogs, menus and popovers.
Do dialogs trap focus?
When a modal opens, focus should move into it, Tab should cycle within it, and closing it should return focus to the element that opened it. A dialog that lets focus wander behind it is disorienting for keyboard users and confusing for screen reader users.
Is there a skip link?
A link at the very top that jumps to the main content saves keyboard users from tabbing through the entire navigation on every page.
Common fixes
Most problems come from a small number of causes. Use native elements wherever possible, because buttons, links and form controls come with keyboard behavior for free. When a custom widget is unavoidable, follow the established interaction patterns for that widget type rather than inventing new ones.
Make it routine
Add a keyboard pass to your definition of done for any new interactive component. It takes minutes per feature, and it prevents a class of bugs that is expensive to fix later when the component is used in fifty places.