How to Make Your Website Keyboard Accessible

Published Jan 8, 2026 5 min read

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.

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

  1. Can you reach every interactive element? Tab through the entire page. Every link, button, form field, and control should receive focus.
  2. Can you see where focus is? A visible outline or highlight should always indicate the currently focused element.
  3. Does the focus order make sense? Focus should flow in a logical reading order, not jump randomly around the page.
  4. Can you operate everything? Try activating every button, link, dropdown, and form. They should all work with Enter or Space.
  5. Can you always escape? Open every modal, dropdown, and menu. You should be able to close them with Escape and continue tabbing.
  6. 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.

Test Your Keyboard Accessibility

Run a free scan to find keyboard and other accessibility issues.

Run Free Scan More Articles