Home WCAG

WCAG 2.1.1 Keyboard

Every functionality available with a pointer must also be available with the keyboard alone, without requiring specific timings for individual keystrokes. Level A; the foundation of every keyboard-accessible widget.

What this Level A criterion requires

Every page function — opening a menu, picking a date, dragging a slider, completing a captcha — must be operable from the keyboard alone. The user must be able to reach the control via Tab, activate it via Enter or Space (or arrow keys for composites), and not be required to time keystrokes (no “press exactly 3 times in 0.5 seconds”).

The companion criterion 2.1.2 No Keyboard Trap (Level A) requires that focus, once entered, can also leave by keyboard. 2.1.4 Character Key Shortcuts (Level A) regulates single-key shortcuts.

Why keyboard parity matters

About 7% of users on desktop and laptop devices rely primarily on keyboard input (WebAIM Screen Reader User Survey #10, 2024). The population includes screen-reader users, switch users, voice- control users, motor-impaired users, and short-term-injury users. A site that loses keyboard parity is unusable for that 7% — and fragile for the other 93% during any moment of pointer unavailability (touchpad failure, sticky mouse button, full-hand typing).

What “operable” means in detail

For each function, three things must work:

  1. Reach. Tab order leads to the control or a way to activate it. Skip-link patterns get past long navigations.
  2. Activate. Enter on links and submit-buttons; Space on non-submit buttons and toggles; arrow keys to navigate within a composite (radio group, listbox, menu, tablist).
  3. Exit. Escape (or Tab out) closes a popup or dismisses a modal. No keyboard trap.

The activation keys are conventional — defined per role in the WAI-ARIA Authoring Practices Guide. A custom widget that ignores these conventions surprises users even when the criterion technically passes.

Common failure modes

  • <div> button. Click handler attached, no tabindex, no keyboard handler. The control is not reachable and not activatable. Use <button>.
  • Hover-only menu. Drop-down opens on mouseenter, never on focus. Add a focus listener; or, better, use a real <button>
    • popover pattern.
  • Drag-only sortable. No keyboard alternative for reordering. Add Move-up / Move-down buttons (see /wcag/2.2/aa/2.5.7).
  • Keyboard trap inside iframe. Focus enters an embedded video player and cannot leave by Tab. The host must provide an exit affordance or use aria-modal correctly.
  • Image-zoom requires pinch. Keyboard +/- or arrow-key pan equivalents required.
  • Custom autocomplete with click-only selection. Add Up/Down to traverse, Enter to select, Escape to dismiss (/aria/combobox).

What is not required

  • The keyboard interaction does not have to be identical to the pointer interaction. A drag can be replaced by a button-based reorder.
  • The criterion does not regulate the quality of the keyboard interaction (efficiency, ergonomics) — only that it exists. WCAG 2.1.4 Character Key Shortcuts addresses single-key shortcuts; WCAG 2.4.3 Focus Order addresses meaningful ordering.

Authoring patterns

Focus management on open/close

When opening a menu or modal, move focus into the new region. When closing, return focus to the trigger.

function openMenu(trigger, menu) {
  menu.hidden = false;
  menu.querySelector('[role="menuitem"]')?.focus();
}
function closeMenu(trigger, menu) {
  menu.hidden = true;
  trigger.focus();
}

The native <dialog> element automates this; custom widgets must do the bookkeeping.

Focus trap inside a modal

modal.addEventListener('keydown', (e) => {
  if (e.key !== 'Tab') return;
  const focusable = [...modal.querySelectorAll(FOCUSABLE_SEL)];
  const first = focusable[0], last = focusable.at(-1);
  if (e.shiftKey && document.activeElement === first) {
    e.preventDefault();
    last.focus();
  } else if (!e.shiftKey && document.activeElement === last) {
    e.preventDefault();
    first.focus();
  }
});

<dialog>.showModal() handles the trap automatically across Chromium, WebKit, and Gecko since 2022.

Composite widget arrow keys

For tablist, listbox, menu, and tree roles, Tab moves into and out of the widget; arrow keys move within. The APG keyboard interaction tables list the exact bindings per role.

Verification

  1. Unplug the mouse.
  2. Press Tab on the home page; can you reach every control?
  3. Activate every control with Enter / Space / arrows as appropriate.
  4. Open every popup; close with Escape; verify focus returns to the trigger.
  5. Confirm :focus-visible shows on every focused element (/wcag/2.2/aa/2.4.7).

About 30% of 2.1.1 violations are detectable by automated tools (missing tabindex on interactive controls); the rest require manual keyboard walkthrough.

Cross-engine support

Native HTML elements are keyboard-accessible by default in Chromium (Blink), WebKit, and Gecko. Differences arise in custom patterns: WebKit on macOS requires the system “Full Keyboard Access” preference for <button> to receive Tab focus; Safari 13+ respects the standard behaviour by default since 2020.

Further reading