Why Keyboard Accessibility Matters
Not everyone uses a mouse. Many people navigate websites using only their keyboard:
- Blind users using screen readers
- Users with motor impairments who can't use a mouse precisely
- Power users who prefer keyboard shortcuts
- Users with temporary injuries (broken arm, RSI)
If your site isn't keyboard accessible, these users simply can't use it. It's also a core WCAG requirement and common source of ADA lawsuits.
The Basics: How Keyboard Navigation Works
Users navigate with these keys:
| Key | Action |
|---|---|
| Tab | Move to next interactive element |
| Shift + Tab | Move to previous interactive element |
| Enter | Activate links and buttons |
| Space | Activate buttons, toggle checkboxes |
| Arrow keys | Navigate within components (menus, tabs, radio groups) |
| Escape | Close modals, menus, dropdowns |
How to Test Keyboard Navigation
Step 1: Put Away Your Mouse
Seriously. Unplug it or move it out of reach. You'll instinctively reach for it otherwise.
Step 2: Start at the Top
- Click in the browser's address bar
- Press Tab to enter the page
- Keep pressing Tab to move through all interactive elements
Step 3: Check These Things
As you tab through, ask yourself:
Can I see where I am?
- Is there a visible focus indicator?
- Is it clearly visible against the background?
Is the order logical?
- Does focus move left-to-right, top-to-bottom?
- Does it follow the visual layout?
Can I access everything?
- Can I reach all links, buttons, and form fields?
- Can I open and navigate dropdown menus?
- Can I use sliders, tabs, and carousels?
Can I get out?
- Can I close modals with Escape?
- Am I ever "trapped" and unable to continue?
Common Issues and How to Fix Them
Issue 1: No Visible Focus Indicator
The focus indicator shows which element is currently selected. Never remove it:
/* Never do this */
*:focus {
outline: none;
}
/* Do this instead - customize but keep visible */
*:focus {
outline: 2px solid #0066cc;
outline-offset: 2px;
}
/* Or use focus-visible for mouse users */
*:focus:not(:focus-visible) {
outline: none;
}
*:focus-visible {
outline: 2px solid #0066cc;
outline-offset: 2px;
}
Issue 2: Interactive Elements Not Focusable
Only certain elements are naturally focusable:
- Links (
<a href>) - Buttons (
<button>) - Form inputs (
<input>,<select>,<textarea>)
If you make other elements interactive, add tabindex="0":
<!-- Bad: div with click handler isn't focusable -->
<div onclick="doSomething()">Click me</div>
<!-- Good: Use a button -->
<button onclick="doSomething()">Click me</button>
<!-- If you must use a div, add tabindex and role -->
<div
role="button"
tabindex="0"
onclick="doSomething()"
onkeydown="if(event.key === 'Enter') doSomething()"
>
Click me
</div>
Issue 3: Wrong Tab Order
Tab order follows the DOM order, not visual order. If your CSS reorders elements visually, the tab order will be confusing.
/* This causes tab order issues */
.sidebar {
order: 2;
}
.main-content {
order: 1;
}
Fix : Match your HTML structure to your visual layout, or use tabindex carefully (though this is hard to maintain).
Issue 4: Keyboard Traps
A keyboard trap occurs when a user can tab into an element but can't tab out. Common culprits:
- Modals that don't trap focus correctly
- Embedded iframes
- Rich text editors
- Video players
// Proper modal focus trapping
const modal = document.querySelector('.modal');
const focusableElements = modal.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
modal.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === firstElement) {
e.preventDefault();
lastElement.focus();
} else if (!e.shiftKey && document.activeElement === lastElement) {
e.preventDefault();
firstElement.focus();
}
}
if (e.key === 'Escape') {
closeModal();
}
});
Issue 5: Dropdown Menus Not Keyboard Accessible
Menus should work with arrow keys, not just Tab:
menu.addEventListener('keydown', (e) => {
const items = menu.querySelectorAll('[role="menuitem"]');
const currentIndex = Array.from(items).indexOf(document.activeElement);
switch (e.key) {
case 'ArrowDown':
e.preventDefault();
items[(currentIndex + 1) % items.length].focus();
break;
case 'ArrowUp':
e.preventDefault();
items[(currentIndex - 1 + items.length) % items.length].focus();
break;
case 'Escape':
closeMenu();
menuButton.focus();
break;
}
});
Issue 6: Skip Link Missing
Users shouldn't have to tab through your entire navigation on every page. Add a skip link:
<!-- First element in body -->
<a href="#main-content" class="skip-link">
Skip to main content
</a>
<!-- Style it to appear on focus -->
<style>
.skip-link {
position: absolute;
top: -40px;
left: 0;
padding: 8px;
background: #000;
color: #fff;
z-index: 100;
}
.skip-link:focus {
top: 0;
}
</style>
<!-- Main content target -->
<main id="main-content">
...
</main>
Testing Tools
Browser DevTools
Chrome DevTools can show tab order:
- Open DevTools (F12)
- Go to Elements panel
- Press Ctrl+Shift+P (Cmd+Shift+P on Mac)
- Type "Show Accessibility" and select it
- Check the accessibility tree
Automated Testing
While automated tools can't fully test keyboard navigation, they can catch:
- Missing focus styles
- Non-focusable interactive elements
- Missing skip links
Scan your site with ClearA11y to identify these issues automatically.
Keyboard Testing Checklist
- All interactive elements are focusable
- Focus indicator is always visible
- Tab order matches visual layout
- No keyboard traps exist
- Dropdown menus work with arrow keys
- Modals can be closed with Escape
- Modals trap focus while open
- Skip link is present and works
- Custom components have proper keyboard support
- Focus moves to new content (modals, alerts)
Make It a Habit
Every time you add a new interactive feature, test it with your keyboard. It takes 30 seconds and catches issues before they ship.
For a complete accessibility audit, scan your website with ClearA11y and get a detailed report of all keyboard and other accessibility issues.
Top comments (0)