How to Make Your Website Keyboard Accessible
If someone can't use a mouse, your entire website needs to work with just a keyboard. And honestly? Most sites fail at this. Whether it's a motor impairment, a tremor, or just a broken arm, if your links, buttons, and menus aren't reachable by keyboard, those users are completely locked out.
In This Article
Why Keyboard Accessibility Matters
You might think this is only about screen reader users, but it's not. A lot of people rely on keyboard navigation:
- People with motor impairments who can't grip or control a mouse
- People with tremors (Parkinson's, essential tremor) who need the precision of keyboard keys
- People with temporary injuries such as a broken arm, RSI, or post-surgical limitations
- Screen reader users who navigate entirely via keyboard commands
- Power users who prefer keyboard shortcuts for speed
- Switch device users who emulate keyboard input through assistive hardware
If even one button or link on your page can't be reached by keyboard, all these people are stuck. And under the ADA, that's a legal risk too. I see keyboard issues constantly in our scans. They're some of the most common WCAG violations out there.
WCAG Requirements
There are a few WCAG 2.2 rules you really need to know about. Let me walk you through the big ones:
SC 2.1.1 Keyboard (Level A)
All functionality must be operable through a keyboard interface, without requiring specific timing for individual keystrokes. The only exception is functionality that inherently requires path-dependent input (like freehand drawing).
SC 2.1.2 No Keyboard Trap (Level A)
If keyboard focus can enter a component, it must be possible to move focus away using only the keyboard. If non-standard keys are needed to exit, the user must be informed.
SC 2.4.3 Focus Order (Level A)
When users tab through the page, the focus order must be logical and meaningful, typically matching the visual reading order (left-to-right, top-to-bottom in English).
SC 2.4.7 Focus Visible (Level AA)
The currently focused element must have a visible focus indicator. Users need to see where they are on the page. See our WCAG mistakes guide for more on focus indicators.
How to Test Keyboard Access
Here's the thing: the best way to test this is stupidly simple. Put your mouse away and try to use your own site. Seriously, just unplug it. Here are the keys you'll need:
| Key | Action |
|---|---|
Tab |
Move to the next focusable element |
Shift + Tab |
Move to the previous focusable element |
Enter |
Activate a link or button |
Space |
Activate a button, toggle a checkbox |
Escape |
Close a modal, dropdown, or popup |
Arrow keys |
Navigate within menus, tabs, radio groups |
What to Check
- Can you reach every interactive element? Tab through the entire page. Every link, button, form field, and control should receive focus.
- Can you see where focus is? A visible outline or highlight should always indicate the currently focused element.
- Does the focus order make sense? Focus should flow in a logical reading order, not jump randomly around the page.
- Can you operate everything? Try activating every button, link, dropdown, and form. They should all work with Enter or Space.
- Can you always escape? Open every modal, dropdown, and menu. You should be able to close them with Escape and continue tabbing.
- Is there a skip link? The very first Tab press should reveal a "Skip to main content" link.
I'd start with your homepage and your most important flows (checkout, signup, search). Once you've done that, run an automated scan to catch the keyboard issues you might have missed.
One thing people forget: test on mobile too. External Bluetooth keyboards paired with phones and tablets behave a bit differently than desktop keyboards. Focus rings sometimes disappear, and custom widgets that work fine on desktop can fall apart on smaller screens. If you're thinking about this, check out my post on mobile accessibility for the full picture.
And while you're testing, try pairing your keyboard test with a screen reader. Fire up VoiceOver on Mac or NVDA on Windows and tab through your site with it running. You'll catch things that keyboard-only testing misses, like unlabeled buttons or confusing announcements. I cover the setup process in my screen reader testing guide.
Common Problems and Fixes
Problem 1: Custom Elements That Aren't Focusable
This one drives me crazy. You've styled a <div> to look like a button, but keyboard users can't reach it at all. They tab right past it.
<!-- Bad: div looks like a button but isn't focusable -->
<div class="btn" onclick="submit()">Submit</div>
<!-- Good: native button is focusable and activatable -->
<button type="submit">Submit</button>
<!-- Acceptable: if you must use a div, add these -->
<div class="btn" role="button" tabindex="0"
onclick="submit()"
onkeydown="if(event.key==='Enter'||event.key===' ')submit()">
Submit
</div>
Rule of Thumb
Just use a real <button>. Seriously. You get keyboard focus, Enter/Space activation, and screen reader announcements for free. A styled <div>? You get none of that, and now you're writing extra code to fake it.
Problem 2: Keyboard Traps
Ever gotten stuck in a modal you couldn't close without a mouse? That's a keyboard trap. Focus goes in, and Tab just cycles endlessly inside the component with no way out. It's incredibly frustrating.
// Modal that properly manages focus
dialog.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeModal();
triggerButton.focus(); // return focus to trigger
}
});
Problem 3: Focus Order Doesn't Match Visual Order
This one is sneaky. You use CSS order, flex-direction: row-reverse, or absolute positioning, and suddenly things look fine visually, but focus follows the DOM order, not the visual order. So keyboard users are tabbing through your page in a totally confusing sequence.
<!-- Bad: visual order is B, A but tab order is A, B -->
<div style="display:flex; flex-direction:row-reverse">
<button>A</button>
<button>B</button>
</div>
<!-- Good: DOM order matches visual order -->
<div style="display:flex">
<button>B</button>
<button>A</button>
</div>
Problem 4: Missing Skip Navigation
Imagine having to tab through 20+ nav links every single time you visit a new page, just to get to the content. That's what your keyboard users deal with if you don't have a skip link. It's exhausting. I wrote a full guide on how to implement skip navigation links if you want the details on getting this right.
<body>
<a href="#main-content" class="skip-link">
Skip to main content
</a>
<header>...navigation...</header>
<main id="main-content">
...page content...
</main>
</body>
Problem 5: onClick Without Keyboard Handling
If you're adding click handlers to non-button elements, keyboard users are out of luck. Those events don't fire on keyboard activation.
// Bad: only works with mouse
card.addEventListener('click', openDetails);
// Good: works with mouse AND keyboard
card.setAttribute('tabindex', '0');
card.setAttribute('role', 'button');
card.addEventListener('click', openDetails);
card.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
openDetails();
}
});
Advanced Patterns
Focus Trapping for Modals
So I just told you keyboard traps are bad, right? Well, here's the exception: modals. When a modal is open, you actually want to trap focus inside it. Tab should cycle through the modal's elements, not escape to the page behind it.
function trapFocus(modal) {
const focusable = modal.querySelectorAll(
'button, a, input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
modal.addEventListener('keydown', function(e) {
if (e.key !== 'Tab') return;
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
});
first.focus();
}
Roving tabindex for Widget Groups
For tab panels, toolbars, and menu bars, you'll want the roving tabindex pattern. The idea is simple: only one item in the group gets tabindex="0", and the rest get tabindex="-1". Users move between items with arrow keys. It keeps your tab order clean and your widgets navigable.
Check Your Keyboard Accessibility
Want to see how your site stacks up? Run a free scan and I'll flag keyboard issues automatically. You can also check out the ADA compliance checklist for the full picture, or read about other common WCAG mistakes I keep finding.