Overview
Every control a mouse user can reach, a keyboard user must reach too, and they must always see where they are. This page covers the keyboard and focus criteria from the accessibility checklist: SC 2.1.1 (Keyboard), SC 2.1.2 (No Keyboard Trap), SC 2.4.1 (Bypass Blocks), SC 2.4.7 (Focus Visible), and SC 2.4.11 (Focus Not Obscured, Minimum). Automated scanners flag few of these reliably, so test them by hand with the Tab key.
Make every control reachable and operable by keyboard; never trap focus
SC 2.1.1 requires every interactive control to be reachable and operable by keyboard alone. SC 2.1.2 bans trapping focus outside a modal.
- Tab and Shift+Tab must reach every control in DOM order.
- Enter and Space must activate buttons; Enter must follow links.
- Arrow keys must navigate inside composite widgets (menus, tabs, sliders).
- Escape must close modals and dismiss menus, returning focus to the trigger.
- Inside a modal, trap focus within the dialog. Outside a modal, never trap.
Native <button>, <a href>, and form elements get this behavior for free. Custom widgets must implement it themselves; see html-aria-patterns for the dialog, tabs, and listbox key contracts.
Keep DOM order equal to visual order
Tab order follows the DOM, not the layout. Do not reorder with CSS order, flex-direction: row-reverse, or position: absolute and expect tab order to follow; a keyboard user sees focus jump around the screen.
Avoid positive tabindex values. They pull elements ahead of the natural order and are hard to maintain. Use tabindex="0" only to make a custom widget focusable, and tabindex="-1" for elements you focus from script, such as a dialog heading or an error summary.
See html for element-level keyboard semantics and css for layout rules that preserve reading order.
Show a visible focus indicator on every interactive element
SC 2.4.7 requires a visible focus indicator on every interactive element. SC 1.4.11 requires the ring to reach 3:1 contrast against adjacent colors.
Style focus with :focus-visible, not :focus. The ring appears for keyboard users; pointer clicks stay clean.
:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 2px;
}Never ship outline: none without a replacement style. Removing the default ring is one of the most common keyboard failures.
Keep focused elements out from under sticky content
SC 2.4.11 (new in WCAG 2.2) requires that the focused component is not fully hidden by author-created content such as sticky headers, cookie bars, or chat launchers.
Reserve space for sticky headers with scroll-padding-top on the scroll container so focused elements scroll into view below them:
html {
scroll-padding-top: 4rem; /* height of the sticky header */
}Dismissible banners should not cover the bottom of the viewport indefinitely; close them or move them out of the flow once the user interacts.
Add a skip link as the first focusable element
SC 2.4.1 requires a way to bypass repeated blocks such as navigation. A skip link lets keyboard users jump straight to <main>:
<a class="skip-link" href="#main">Skip to content</a>
<!-- ... header and nav ... -->
<main id="main">...</main>Reveal it on focus and hide it off-screen otherwise. See css for the off-screen clip pattern. Landmark elements (<nav>, <main>) also help screen reader users skip, but sighted keyboard users need the visible link.