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
- Screen reader users — navigate entirely via keyboard
- Users with motor disabilities — may use keyboard, switch devices, or voice control that emulates keyboard input
- 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
- Disconnect or ignore your mouse. Starting from the address bar, press Tab repeatedly to move through every interactive element on the page.
- Verify each element: buttons activate with Enter or Space, links with Enter, dropdowns with Space/Arrow keys, custom widgets with Arrow keys and Escape.
- Press Shift+Tab to navigate backward and confirm reverse order works.
- Check that every interactive element (links, buttons, inputs, menus, modals, tabs, sliders) is reachable and operable.
- Pass: Every interactive element is reachable and operable with keyboard alone.
- 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.