Keyboard Accessibility Testing: A Complete Guide for Developers

Blogueguides

Keyboard Accessibility Testing: A Complete Guide for Developers

Here is something that catches a lot of teams off guard: roughly 15 to 20 percent of accessibility lawsuits filed in the US cite keyboard navigation failures. Not missing alt text, not color contrast — keyboard access. The reason is simple. When a user cannot operate your site without a mouse, the entire experience collapses. And it is not just screen reader users who rely on the keyboard. People with motor impairments, repetitive strain injuries, power users, and anyone with a broken trackpad all depend on keyboard navigation working correctly.This guide covers everything you need to know about testing keyboard accessibility, from the underlying WCAG requirements to hands-on testing techniques and code fixes for the most common failures.

Why Keyboard Accessibility Matters

WCAG 2.2 Level A includes two success criteria that directly address keyboard access:

  • 2.1.1 Keyboard — All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes.
  • 2.1.2 No Keyboard Trap — If keyboard focus can be moved to a component using a keyboard interface, then focus can be moved away from that component using only a keyboard interface. If it requires more than unmodified arrow or tab keys, the user is advised of the method for moving focus away.

At Level AA, there is an additional requirement:

  • 2.4.7 Focus Visible — Any keyboard-operable user interface has a mode of operation where the keyboard focus indicator is visible.

And WCAG 2.2 introduced:

  • 2.4.11 Focus Not Obscured (Minimum) — When a user interface component receives keyboard focus, the component is not entirely hidden due to author-created content.
  • 2.4.13 Focus Appearance — When a UI component receives keyboard focus, the focus indicator meets minimum area and contrast requirements.

Failing any of these makes your site non-compliant. But beyond compliance, poor keyboard support alienates a significant portion of your users who simply cannot use a mouse.

Understanding Tab Order and Focus Flow

When a user presses the Tab key, the browser moves focus to the next focusable element in what is called the tab order. By default, the tab order follows the DOM order — the sequence in which elements appear in the HTML source. This is usually the right behavior, but CSS layouts can create visual orders that diverge from the DOM, leading to confusing tab sequences.

Natively Focusable Elements

The browser makes these elements focusable by default, with no extra markup needed:

  • <a href="..."> — Links with an href attribute
  • <button> — Buttons
  • <input>, <select>, <textarea> — Form controls
  • <details> / <summary> — Disclosure widgets
  • Any element with tabindex="0"

A common mistake is using <div> or <span> elements as interactive controls. These are not focusable by default, so keyboard users simply skip over them. If you must use a non-semantic element as a button, you need to add tabindex="0", role="button", and handle both click and keydown events for Enter and Space keys.

The tabindex Pitfall

You might be tempted to use positive tabindex values to force a specific tab order. Do not do this. Positive tabindex values (1, 2, 3, etc.) override the natural DOM order and create maintenance nightmares. Every element with a positive tabindex gets focused before any element with tabindex 0 or no tabindex, which means adding a single tabindex="5" somewhere can break the navigation flow for the entire page.

The only tabindex values you should use:

  • tabindex="0" — Makes a non-focusable element focusable in the natural DOM order.
  • tabindex="-1" — Makes an element programmatically focusable (via JavaScript .focus()) but removes it from the tab order. Useful for headings in single-page apps, error messages, or modal containers.

How to Test Keyboard Accessibility

Step 1: Put Away the Mouse

This sounds obvious, but it is the most important step. Unplug your mouse or move it out of reach. Then navigate your entire site using only the keyboard. You will quickly discover pain points that are invisible when you can click.

Step 2: Tab Through Every Page

Press Tab to move forward through the page. Press Shift+Tab to move backward. For each page, verify:

  • Can you reach every interactive element (links, buttons, form fields, menus)?
  • Does the tab order follow a logical, predictable sequence?
  • Is there a visible focus indicator on every focused element?
  • Are there any keyboard traps where you get stuck?

Step 3: Test Interactive Components

Standard keyboard interactions that must work:

ComponentExpected Keyboard Behavior
LinksEnter to activate
ButtonsEnter or Space to activate
CheckboxesSpace to toggle
Radio buttonsArrow keys to move between options
Dropdowns (select)Arrow keys to navigate, Enter to select
Tabs (tab panel)Arrow keys between tabs, Tab to enter panel
MenusArrow keys to navigate, Enter to select, Escape to close
Modals/dialogsEscape to close, Tab trapped within modal
AccordionsEnter or Space to expand/collapse
SlidersArrow keys to adjust value

Step 4: Check Skip Navigation

A skip navigation link should be the very first focusable element on the page. When activated, it moves focus past the header and navigation to the main content area. This is required by WCAG 2.4.1 (Bypass Blocks).

Test it: press Tab once on page load. A skip link should become visible. Press Enter and verify that focus moves to the main content.

Step 5: Verify Focus Management in Dynamic Content

Single-page applications and dynamically loaded content are where keyboard accessibility most commonly breaks. When content changes without a page reload, focus must be managed manually. Situations to test:

  • Modal dialogs — Focus should move to the modal when it opens and return to the triggering element when it closes.
  • Toast notifications — Should use aria-live regions rather than stealing focus.
  • Infinite scroll — New content should not reset the user's focus position.
  • Client-side routing — Focus should move to the new page's main heading or content after navigation.

Common Keyboard Accessibility Failures and Fixes

Failure: Click-Only Event Handlers

One of the most common failures. A div or span has an onclick handler but no keyboard equivalent.

<!-- Bad: not keyboard accessible -->
<div class="card" onclick="openDetails()">Click to view</div>

<!-- Good: semantic button, keyboard accessible by default -->
<button class="card" onclick="openDetails()">View details</button>

<!-- Acceptable: div with full keyboard support -->
<div class="card" role="button" tabindex="0"
     onclick="openDetails()"
     onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();openDetails()}">
  View details
</div>

The semantic button approach is always preferable. It gives you keyboard support, focus styling, and screen reader announcements for free.

Failure: Invisible Focus Indicators

Many websites remove the browser's default focus outline for aesthetic reasons and forget to replace it with something visible.

/* Bad: removes focus indicator entirely */
*:focus {
  outline: none;
}

/* Good: custom focus style that meets WCAG 2.4.13 */
*:focus-visible {
  outline: 3px solid #1a73e8;
  outline-offset: 2px;
  border-radius: 2px;
}

/* This preserves aesthetics for mouse users
   while showing focus for keyboard users */

The :focus-visible pseudo-class is well-supported in modern browsers and only shows the focus ring when the user is navigating with a keyboard, not when clicking with a mouse.

Failure: Keyboard Traps

A keyboard trap occurs when a user can Tab into a component but cannot Tab out of it. This most commonly happens with:

  • Embedded media players (especially older Flash-based ones)
  • Third-party widgets (chat widgets, social embeds)
  • Poorly implemented modal dialogs
  • Custom WYSIWYG editors

The fix for modal dialogs involves intentional focus trapping — keeping focus within the modal while it is open, but releasing it when the modal closes:

function trapFocus(modalElement) {
  const focusableElements = modalElement.querySelectorAll(
    'a[href], button:not([disabled]), input:not([disabled]),
     select:not([disabled]), textarea:not([disabled]),
     [tabindex]:not([tabindex="-1"])'
  );
  const firstFocusable = focusableElements[0];
  const lastFocusable = focusableElements[focusableElements.length - 1];

  modalElement.addEventListener('keydown', function(e) {
    if (e.key === 'Tab') {
      if (e.shiftKey && document.activeElement === firstFocusable) {
        e.preventDefault();
        lastFocusable.focus();
      } else if (!e.shiftKey && document.activeElement === lastFocusable) {
        e.preventDefault();
        firstFocusable.focus();
      }
    }
    if (e.key === 'Escape') {
      closeModal();
    }
  });

  firstFocusable.focus();
}

Failure: Missing Skip Navigation

Without a skip link, keyboard users have to Tab through every navigation item on every page load. Here is a minimal implementation:

<!-- HTML: first element inside body -->
<a href="#main-content" class="skip-link">Skip to main content</a>

<!-- Main content area -->
<main id="main-content" tabindex="-1">
  <h1>Page Title</h1>
  ...
</main>
/* CSS: hidden until focused */
.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  background: #000;
  color: #fff;
  padding: 8px 16px;
  z-index: 100;
  transition: top 0.2s;
}

.skip-link:focus {
  top: 0;
}

The tabindex="-1" on the main element ensures that the skip link target actually receives focus in all browsers, including Safari.

Failure: Custom Dropdowns Without Arrow Key Support

Custom dropdown menus built with divs often only respond to mouse clicks. A keyboard-accessible dropdown must support:

  • Enter or Space to open
  • Arrow Down/Up to move through options
  • Enter to select
  • Escape to close without selecting
  • Home/End to jump to first/last option
  • Type-ahead — pressing a letter key jumps to the first option starting with that letter

If you find yourself rebuilding all of this, consider using the native <select> element instead. It handles all of these interactions automatically and is significantly more robust.

Automated Tools for Keyboard Testing

While manual testing is essential, several tools can identify keyboard accessibility issues programmatically:

  • axe DevTools — Flags missing keyboard handlers, focus order issues, and missing skip links. Available as a browser extension and CI integration.
  • IBM Equal Access Checker — Includes specific keyboard-related rules and provides detailed remediation guidance.
  • Lighthouse Accessibility Audit — Checks for tabindex misuse, missing focus indicators, and elements that lack keyboard event handlers.
  • Pa11y — CLI tool that can be integrated into build pipelines to catch regressions.

None of these replace manual Tab testing. Use them as a safety net, not a substitute.

Building a Keyboard Testing Workflow

For teams that want to integrate keyboard testing into their development process, here is a practical workflow:

During Development

  • Use semantic HTML elements by default. Every time you reach for a div with an onclick, stop and ask whether a button or link would work instead.
  • Never remove outline without adding a :focus-visible replacement.
  • Test new components with Tab, Shift+Tab, Enter, Space, Escape, and Arrow keys before marking them done.

In Code Review

  • Check that all interactive elements are natively focusable or have proper tabindex, role, and keydown handlers.
  • Verify that any outline: none in CSS has a corresponding :focus-visible replacement.
  • Look for positive tabindex values and flag them as bugs.

In QA

  • Run a full Tab-through of each page template.
  • Test all modals, dropdowns, tabs, and accordions with keyboard only.
  • Verify skip navigation on every unique page layout.
  • Test with browser zoom at 200 percent to ensure focus indicators remain visible.

In CI/CD

  • Run axe-core or Pa11y in your test pipeline to catch regressions.
  • Flag any new element with onclick that lacks a corresponding keyboard handler.
  • Monitor for outline: none in CSS changes.

Keyboard Accessibility Checklist

Use this checklist when auditing any page:

  • All interactive elements are reachable via Tab
  • Tab order follows a logical visual sequence
  • No keyboard traps exist anywhere on the page
  • Visible focus indicator on every focusable element
  • Focus indicator meets WCAG 2.4.13 contrast and size requirements
  • Skip navigation link is the first focusable element
  • Skip link moves focus to main content area
  • Modal dialogs trap focus and release it on close
  • Dropdown menus support arrow key navigation
  • Custom widgets follow ARIA Authoring Practices keyboard patterns
  • No positive tabindex values in the codebase
  • Focus is managed correctly during client-side route changes
  • Dynamic content updates do not steal or reset focus
  • All functionality available by mouse is also available by keyboard

Moving Forward

Keyboard accessibility is not a feature you bolt on at the end of a project. It is a fundamental design constraint that should influence decisions from the wireframe stage through deployment. The good news is that if you stick with semantic HTML — real buttons, real links, real form elements — you get most keyboard accessibility for free. The problems almost always come from reinventing the wheel with divs and spans.

Start with one page today. Put away your mouse. Tab through it. Fix what breaks. Then do the next page. That incremental approach, consistently applied, will bring your site into compliance faster than any retrofit project planned for next quarter.

Scan Your Website for Free

Get an instant WCAG 2.1 compliance report, no signup required

Start Free Scan