Press Tab on your own site. If you can't tell where you are after two or three presses, neither can anyone who navigates by keyboard. Somewhere in the CSS, a focus ring was switched off and nothing replaced it.
Web HIG tip #10 (Quick #72 / HIG-A11Y-006): Focus must be visible on every interactive element — never outline: none without a custom ring.
Why it matters
The focus ring is the keyboard user's cursor:
- People who use a keyboard, switch device, or voice control rely on it to see which link, button, or field will act next
- Without it, pressing Enter is a guess, and a wrong guess can submit, delete, or buy
-
outline: noneis often added to stop the ring showing on mouse clicks, and that also hides it from everyone who needs it - Rings hidden under a sticky header or cookie banner fail the same way
The full HIG (§5.3, Keyboard Focus & Interaction) requires an explicit, high-contrast indicator on every interactive control. The ring must contrast with what's next to it and must not be hidden by other elements. §5.1 sets the contrast floor for focus indicators at 3:1, the figure from WCAG 1.4.11 Non-text Contrast (AA); Quick #62 is the companion rule. On the WCAG side, this builds on 2.4.7 Focus Visible and 2.4.11 Focus Not Obscured (Minimum), both Level AA in WCAG 2.2. WCAG's minimum only asks that the focused element isn't completely hidden; the HIG asks for more and keeps the ring itself uncovered.
The rule of thumb
Ask: if I unplug the mouse, can I always see where I am?
If you removed the default ring, put a better one back.
/* Don't: the ring is gone for everyone */
button:focus {
outline: none;
}
/* Do: a clear ring for keyboard focus, from one shared color */
:focus-visible {
outline: 3px solid var(--focus-ring);
outline-offset: 2px;
}
Delete the outline: none rule rather than adding a ring next to it. button:focus is more specific than :focus-visible and will still win.
:focus-visible lets the browser decide when a ring helps. It shows for keyboard focus and usually not for a mouse click on a button, which is what most people wanted from outline: none in the first place. Text fields still show it on click, because you need to know where you're typing.
If a sticky header covers whatever is focused, leave room for it:
/* Keep focused elements from scrolling under a 4rem sticky header */
html {
scroll-padding-top: 4rem;
}
Match it to the header's real height (a custom property helps), and use scroll-padding-bottom for bottom bars and cookie banners.
Do this instead
- Delete
outline: noneandoutline: 0unless a visible ring still applies on keyboard focus (for example, suppress only with:focus:not(:focus-visible)and style:focus-visible). - Style
:focus-visiblewith one shared focus-ring color (a CSS custom property works well), so it's contrast-checked once, in light and dark mode - Prefer
outlinetobox-shadowfor the ring: Windows high-contrast (forced colors) mode drops box shadows, so a shadow-only ring can vanish. If you keep a box-shadow ring, addoutline: 2px solid transparentas well. Forced colors mode repaints it in a visible system color. - Use
outline-offsetso the ring isn't lost against a button's own border or background - Check that sticky headers, footers, and banners don't cover the focused element
Quick check for your app
Put the mouse aside and Tab through a whole page, including a menu, a dialog, and a form. At every stop, you should see where focus is without hunting for it, and, as the HIG asks, nothing should sit on top of the ring. Then repeat in dark mode. A tool can find outline: none in your CSS, but only tabbing through tells you whether people can actually see where they are.
The Web HIG is an open set of guidelines for how the web should behave, not a component library. Design systems define how it looks. The Web HIG defines how it behaves.
Top comments (0)