Operable · Level AA · 2.4.7
Focus Visible
Whatever currently has keyboard focus needs a visible indicator, and that indicator can't be removed without something at least as visible put in its place.
- Motor
- Cognitive
How to recognize it
Tab through the page and watch for a visible change on whatever currently has focus. The single most common failure is a CSS reset somewhere in the codebase with *:focus { outline: none; } and nothing to replace it — the browser's default focus ring gets stripped and nothing takes its place, so a keyboard user simply can't tell where they are. A subtler version: an indicator technically exists but is so low-contrast against its background (a pale gray border on white) that it's effectively invisible, especially for anyone with low vision. A third, easy-to-miss version: the indicator is there in the CSS and would be visible, but a parent element with overflow: hidden — a card carousel, a scrollable list — clips it off, so the outline gets cut short or disappears entirely for items near the edge.
How to fix it
Never remove the default focus outline without replacing it with something equally or more visible. If the goal is to stop showing a ring on mouse clicks while keeping it for keyboard users, use :focus-visible instead of :focus — that's exactly the distinction it exists for: :focus-visible { outline: ...} shows the indicator for keyboard interaction without necessarily showing it for a mouse click, so there's no need to remove focus styles outright. The indicator itself needs at least 3:1 contrast against its adjacent background (that's 1.4.11's territory, but it applies directly here) — a pale gray ring on a white background won't clear that bar. And check that no ancestor element clips it with overflow: hidden; either give the focused element enough surrounding space, or adjust the container so the outline has room to render.
Example
Avoid
*:focus {
outline: none;
}.custom-checkbox:focus {
border-color: #e5e5e5; /* ~1.2:1 against white — practically invisible */
}.carousel {
overflow: hidden; /* clips the focus ring on the last visible card */
}Prefer
a:focus {
outline: none;
}
a:focus-visible {
outline: 2px solid #1a1a1a;
outline-offset: 2px;
}.custom-checkbox:focus-visible {
outline: 3px solid #b45309; /* 5:1 against white */
outline-offset: 2px;
}.carousel {
overflow-x: auto;
padding: 4px; /* room for the focus ring so it isn't clipped */
}