Keyboard Navigation Testing: A Developer Complete Guide to WCAG Operability

Published: (June 12, 2026 at 08:14 AM EDT)
3 min read
Source: Dev.to

Source: Dev.to

Keyboard accessibility is one of the most important — and most neglected — aspects of web accessibility. An estimated 2.5 million Americans have motor disabilities that prevent mouse use. If your site can’t be operated entirely by keyboard, you’re excluding them completely. WCAG 2.2 Principle 2 (Operable) contains the keyboard requirements: 2.1.1 Keyboard (AA): All functionality must be operable via keyboard 2.1.2 No Keyboard Trap (AA): If focus moves into a component, it must be possible to move it out 2.4.3 Focus Order (AA): If page can be navigated sequentially, order must be logical and predictable 2.4.7 Focus Visible (AA): Any keyboard-operable UI must have a visible focus indicator 2.4.11 Focus Appearance (AA, new in 2.2): Focus indicator must meet size and contrast requirements Start with the basic keyboard test: Unplug (or ignore) your mouse Press Tab to move forward through interactive elements Press Shift+Tab to move backward Use Enter/Space to activate buttons, links, checkboxes Use arrow keys for radio groups, menus, sliders Use Escape to close dialogs and menus Any element you can’t reach or activate? That’s a WCAG 2.1.1 failure. // ❌ Keyboard inaccessible function Dropdown({ items }) { return (

  {items.map(item => (
     select(item)}>{item.label}
  ))}

); }

// ✅ Fully keyboard accessible function Dropdown({ items }) { return (

    {items.map((item, i) => (
       e.key === 'Enter' && select(item)}
      >
        {item.label}
      
    ))}
  

); }

Modal dialogs must: Move focus into the dialog when it opens Trap focus inside while it’s open (Tab cycles within) Return focus to the trigger element when it closes

function openModal(modalEl, triggerEl) { const focusable = modalEl.querySelectorAll( ‘button, [href], input, select, textarea, [tabindex]:not([tabindex=“-1”])’ ); const first = focusable[0]; const last = focusable[focusable.length - 1];

first.focus();

modalEl.addEventListener(‘keydown’, (e) => { if (e.key === ‘Tab’) { if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } } if (e.key === ‘Escape’) closeModal(triggerEl); }); }

The single most common mistake: outline: none in a CSS reset. /* ❌ Never do this globally */

  • { outline: none; }

/* ✅ Remove default, replace with better style */ :focus { outline: none; } :focus-visible { outline: 3px solid #0066CC; outline-offset: 2px; border-radius: 2px; }

The :focus-visible pseudo-class shows focus only when navigating by keyboard, not on mouse click — giving you the best of both worlds. Users navigating by keyboard should be able to skip repetitive navigation. A skip link is the first focusable element in your page: Skip to main content

.skip-link { position: absolute; top: -40px; left: 0; background: #000; color: #fff; padding: 8px 16px; z-index: 9999; } .skip-link:focus { top: 0; }

Automated scanners can catch about 40% of keyboard accessibility issues — primarily missing tabindex, incorrect roles, and missing focus styles. Tools like AccessiScan provide a starting point with 201 automated checks, but the Tab-through test above is still essential for catching interaction patterns that automation misses.

0 views
Back to Blog

Related posts

Read more »

Introduction to Git

Welcome to Git Mastery, a series where we'll learn Git from the ground up, starting with the absolute basics and gradually moving toward advanced workflows, Git...