Keyboard And Focus
Keyboard Operability and Focus — WCAG 2.2
Source: www.w3.org/TR/WCAG22/ §2.1.1, §2.1.2, §2.4.7, §2.4.11
Quick reference
| Criterion | Requirement | Level |
|---|---|---|
| 2.1.1 | All functionality operable by keyboard | A |
| 2.1.2 | No keyboard trap — focus can always leave | A |
| 2.4.7 | Focus indicator is visible | AA |
| 2.4.11 | Focused component is not entirely hidden (New in 2.2) | AA |
2.1.1 Keyboard — Level A
All functionality of the content is operable through a keyboard interface without requiring specific timings for individual keystrokes.
Exception: Underlying functions that depend on the path of movement (not just endpoints), such as freehand drawing or signature capture.
Implementation notes:
- Every interactive element — buttons, links, form fields, custom controls — must be reachable and operable by keyboard
- Required keyboard patterns by role (from ARIA APG):
- Button:
EnterorSpaceto activate - Link:
Enterto activate - Checkbox:
Spaceto toggle - Radio group: arrow keys to move between options
- Modal dialog:
Tab/Shift+Tabwithin dialog;Escapeto close - Menu: arrow keys to navigate;
Escapeto close;Enter/Spaceto select - Tab panel: arrow keys between tabs;
Tabenters panel content
- Button:
- Custom components built with
divorspanmust addtabindex="0"and keyboard event handlers - Drag-and-drop interactions must have a keyboard alternative
2.1.2 No Keyboard Trap — Level A
If keyboard focus can be moved to a component using a keyboard, then focus can be moved away using only the keyboard. If non-standard exit keys are required, the user must be informed of the method.
Note: This criterion applies to the entire page — any component that traps keyboard focus breaks the page for keyboard users, regardless of other conformance. It is listed as a non-interference requirement in WCAG conformance section 5.2.
Implementation notes:
- Modal dialogs: trap focus within the dialog (correct behavior), but must release on close. Trapping focus in a dialog is intentional and correct — what 2.1.2 forbids is making it impossible to close.
- Rich text editors, custom date pickers, embedded widgets: ensure
Escapeexits or provide clear instructions - iframes: focus must not become permanently stuck — user must be able to tab out
- Test with keyboard only: Tab through the entire page and confirm you can reach and leave every interactive element
2.4.7 Focus Visible — Level AA
Any keyboard-operable user interface has a mode of operation where the keyboard focus indicator is visible.
Implementation notes:
- Never use
outline: noneoroutline: 0on focused elements without providing an alternative visible focus style - Browser default focus outlines satisfy this criterion — removing them without replacement is the most common violation
- Acceptable alternative focus styles: custom
outline,box-shadowring,borderchange,background-colorchange — anything that makes the focused state visually distinct - The criterion requires a mode where focus is visible — most implementations satisfy this by making focus always visible. Some implementations show focus only for keyboard navigation (
:focus-visibleCSS pseudo-class) — this is acceptable.
CSS pattern — custom focus ring:
:focus-visible {
outline: 2px solid #005fcc;
outline-offset: 2px;
}
Do not:
/* Never do this without replacement */
* { outline: none; }
button:focus { outline: none; }
2.4.11 Focus Not Obscured (Minimum) — Level AA (New in WCAG 2.2)
When a UI component receives keyboard focus, the component is not entirely hidden due to author-created content.
What this means:
- Sticky headers, cookie banners, fixed navigation bars, and chat widgets must not completely obscure the focused element
- Partial obscuration is permitted at AA — the focused element just cannot be entirely covered
- Full non-obscuration (no part hidden) is required at AAA (2.4.12 — not covered here)
Implementation notes:
- Use
scroll-margin-toporscroll-padding-topon the page or scroll container to account for sticky header height when focus causes scrolling - Fixed banners at the bottom of the viewport: ensure elements near the bottom of the content area have enough scroll-margin-bottom
scroll-margin-top: 80px(where 80px matches the sticky header height) is the standard fix- Test by tabbing through a page with a sticky header and confirming no focused element is fully hidden
/* Accommodate sticky header (80px tall) */
html {
scroll-padding-top: 88px; /* header + 8px buffer */
}