2.1.1 Keyboard

Level: A | Principle: Operable | Since: WCAG 2.0 | Automation: Partial


What This Means

All functionality of the content must be operable through a keyboard interface without requiring specific timing for individual keystrokes. If it works with a mouse, it must also work with a keyboard. The only exception is functionality that depends on the path of the user's movement (such as freehand drawing), not just the endpoints.

This is fundamental to accessibility — users who cannot use a mouse rely entirely on keyboard navigation.

Who This Affects

  1. Screen reader users — navigate entirely via keyboard
  2. Users with motor disabilities — may use keyboard, switch devices, or voice control that emulates keyboard input
  3. Power users — prefer keyboard shortcuts for efficiency

Common Pitfalls

1. Click handlers on non-interactive elements

<!-- Bad: div is not keyboard focusable or operable -->
<div onclick="openMenu()">Menu</div>

<!-- Good: use a button -->
<button onclick="openMenu()">Menu</button>

2. Mouse-only event handlers

// Bad: only responds to mouse
element.addEventListener('mouseover', showTooltip);
element.addEventListener('mouseout', hideTooltip);

// Good: also responds to keyboard
element.addEventListener('mouseover', showTooltip);
element.addEventListener('focus', showTooltip);
element.addEventListener('mouseout', hideTooltip);
element.addEventListener('blur', hideTooltip);

3. Custom controls without keyboard support

<!-- Bad: custom dropdown with no keyboard interaction -->
<div class="dropdown" onclick="toggle()">
  <div class="option">Option 1</div>
  <div class="option">Option 2</div>
</div>

<!-- Good: use native select or add ARIA + keyboard handlers -->
<select>
  <option>Option 1</option>
  <option>Option 2</option>
</select>

4. Drag-and-drop without keyboard alternative

Drag-and-drop functionality must have a keyboard-accessible alternative, such as move up/down buttons or a reorder dialog.

How to Fix

Use semantic HTML elements

<!-- Good: native elements are keyboard accessible by default -->
<a href="/page">Link</a>
<button type="button">Action</button>
<input type="text">
<select>...</select>

Add keyboard support to custom widgets

<!-- Good: custom button with keyboard support -->
<div role="button" tabindex="0"
     onclick="doAction()"
     onkeydown="if(event.key==='Enter'||event.key===' ') doAction()">
  Custom Action
</div>

How to Test

  1. Disconnect or ignore your mouse. Starting from the address bar, press Tab repeatedly to move through every interactive element on the page.
  2. Verify each element: buttons activate with Enter or Space, links with Enter, dropdowns with Space/Arrow keys, custom widgets with Arrow keys and Escape.
  3. Press Shift+Tab to navigate backward and confirm reverse order works.
  4. Check that every interactive element (links, buttons, inputs, menus, modals, tabs, sliders) is reachable and operable.
  5. Pass: Every interactive element is reachable and operable with keyboard alone.
  6. Fail: Any element is skipped, unreachable, or requires a mouse to operate.

axe-core Rules

| Rule | What It Checks | |------|---------------| | accesskeys | Ensures every accesskey attribute value is unique | | server-side-image-map | Ensures server-side image maps are not used (they are not keyboard accessible) |

Tools

Test this criterion with the Focus Order Visualizer.

Sources

  1. W3C WCAG 2.2 — Understanding 2.1.1
  2. W3C Technique G202: Ensuring keyboard control for all functionality
  3. axe-core: accesskeys
  4. axe-core: server-side-image-map