Keyboard accessibility is the foundation of web accessibility. Every user interaction that can be performed with a mouse must also be available to keyboard users. This is not just a WCAG requirement (Success Criterion 2.1.1 Keyboard, Level A) — it is the single most impactful accessibility practice you can adopt. Screen reader users navigate primarily by keyboard. People with motor impairments use keyboards, switch devices, or voice input that maps to keyboard events. And many power users simply prefer the keyboard for speed. If your interface breaks without a mouse, you have excluded a significant portion of your audience.
Tab Order and tabindex
The tab order of a page is the sequence in which elements receive focus when a user presses the Tab key. By default, interactive elements — links, buttons, form controls — appear in the tab order based on their position in the DOM. This default order is usually correct if your DOM structure matches your visual layout.
The tabindex attribute modifies the tab order, but it should be used with extreme care:
- tabindex="0": Adds an element to the natural tab order based on its DOM position. Use this when you need a non-interactive element (like a div) to be focusable — for example, a custom component that handles keyboard events.
- tabindex="-1": Makes an element programmatically focusable (via JavaScript's focus() method) but removes it from the tab order. This is essential for focus management — moving focus to a heading after a page navigation, or to an error message after form validation.
- tabindex with a positive value (1, 2, 3...): Do not use this. It overrides the natural DOM order and creates maintenance nightmares. Every time you add an element, you need to recalculate all the tabindex values. Use DOM order to control tab sequence instead.
Skip Links
Skip links allow keyboard users to bypass repetitive navigation and jump directly to the main content of a page. Without a skip link, a keyboard user visiting your site must Tab through every navigation item, logo link, and utility link before reaching the content they came for — on every single page load. This is the equivalent of forcing someone to walk through the entire lobby of a building before reaching the elevator, every single time they visit.
Implementing a skip link is straightforward: place an anchor link as the first focusable element on the page that points to the main content area. The link can be visually hidden by default and made visible when it receives focus, so it does not affect the visual design but is immediately available to keyboard users. WCAG Success Criterion 2.4.1 Bypass Blocks (Level A) requires a mechanism to bypass repeated content, and a skip link is the most common solution.
Focus Management in Single-Page Applications
Single-page applications (SPAs) present unique keyboard accessibility challenges because page navigations do not trigger a full page load. When a user clicks a link in a traditional multi-page site, the browser resets focus to the top of the new page and announces the new page title. In an SPA, the DOM is updated dynamically, and the browser does not know that a "page" has changed. The result: after navigating, focus stays where it was (often on the link that was just clicked, which may no longer be visible), and screen readers do not announce anything.
To fix this, you need to manage focus explicitly on route changes. Common patterns include:
- Moving focus to the main heading (h1) of the new "page." Use tabindex="-1" on the heading so it can receive focus programmatically without entering the tab order.
- Announcing the route change to screen readers using a live region that reads the new page title.
- Moving focus to the main content container. This is less ideal than the heading approach because it does not give screen reader users an immediate sense of where they are.
Modal Focus Trapping
When a modal dialog opens, keyboard focus must be moved into the dialog and constrained within it until the dialog closes. This is called focus trapping, and it is required by WCAG Success Criterion 2.1.2 No Keyboard Trap (Level A) — with the important nuance that the user must always be able to close the dialog (typically with the Escape key) to escape the trap.
A proper focus trap works like this:
- When the modal opens, move focus to the first focusable element inside the modal (or to the modal container itself if it has a label).
- When the user Tabs past the last focusable element in the modal, wrap focus back to the first focusable element. When they Shift+Tab past the first element, wrap to the last.
- When the modal closes, return focus to the element that triggered the modal. This is critical — if focus is not restored, keyboard users lose their place on the page and have to start navigating from the top.
- Ensure the Escape key closes the modal. This is a standard expectation that screen reader users and keyboard users rely on.
Custom Keyboard Interactions
Some complex widgets require keyboard interactions beyond Tab and Enter. The WAI-ARIA Authoring Practices Guide defines expected keyboard behavior for common patterns:
- Tab interfaces: Tab key moves focus to the tab list. Arrow keys move between tabs. Tab activates the selected tab and moves focus into the tab panel.
- Menu bars and drop-down menus: Arrow keys navigate between menu items. Enter or Space activates the focused item. Escape closes the menu and returns focus to the trigger.
- Tree views: Arrow Up and Down move between visible nodes. Arrow Right expands a collapsed node. Arrow Left collapses an expanded node.
- Data grids: Arrow keys move between cells. Tab exits the grid. Enter enters edit mode on the current cell.
When implementing custom keyboard interactions, always ensure they are discoverable. If a widget has keyboard shortcuts, document them and consider providing an onscreen hint.
Testing with Keyboard Alone
The most effective keyboard accessibility test is the simplest: unplug your mouse (or disable your trackpad) and try to use your application. Complete your core user flows using only Tab, Shift+Tab, Enter, Space, Escape, and arrow keys. As you navigate, check: Can I reach every interactive element? Can I see where focus is at all times? Can I operate every control? Can I dismiss overlays and modals? Does focus move logically? Can I get to the main content without tabbing through the entire navigation? Make this part of your regular QA process, and you will catch the majority of keyboard accessibility issues before they reach your users.