:focus-visible Ends the outline: none Wars
For years, web accessibility had a recurring tragedy in three acts. Act one: browser draws focus ring when you click a button. Act two: designer says it's ugly, developer writes *:focus { outline: none }. Act three: every keyboard user loses the ability to see where they are on the page.
The root problem was that :focus can't tell how focus arrived. Clicking focuses, tabbing focuses - same pseudo-class. :focus-visible fixes exactly that:
/* Remove the ring ONLY when the browser deems it unnecessary (mouse/touch) */
:focus:not(:focus-visible) {
outline: none;
}
/* And make the keyboard ring genuinely good */
:focus-visible {
outline: 2px solid #4f46e5;
outline-offset: 2px;
border-radius: inherit;
}
Click a button: no ring. Tab to it: clear ring. Both audiences satisfied, no JavaScript, no focus-trap hacks.
When does it match?
The heuristic is the browser's, and it's smarter than "keyboard vs mouse":
- Tab / Shift-Tab focus: matches.
- Mouse click on a button: doesn't match.
- Mouse click on a text input: matches anyway - because you're about to type, the browser treats it as keyboard interaction. This surprises people, but it's the right call.
- Focus moved by script right after a keypress: generally matches.
You may not need the :not() rule at all
Here's the part that's genuinely little-known: modern browsers' default focus ring already uses :focus-visible internally. If you never wrote outline: none in the first place, clicking buttons hasn't shown rings for years. The cleanup rule exists for codebases that carry old resets - which is most of them. Audit for outline: none, replace with the pair above, done.
Styling notes from practice
.btn:focus-visible {
outline: 2px solid currentColor; /* adapts to the button's own color */
outline-offset: 2px;
}
- Prefer outline over box-shadow for rings: outline doesn't affect layout, follows border-radius in all modern engines now, and survives overflow clipping better.
- outline-offset creates the gap that makes rings look designed instead of default.
- Test with a real Tab-through of your page. It takes ninety seconds and instantly reveals whether your interactive elements are reachable and visible - the cheapest accessibility audit that exists.
Support: every browser since 2022. This belongs in the same family as accent-color - platform features that let you stop choosing between design taste and accessibility.
Full-stack web developer sharing practical tutorials and building tools that ship.
Got something on your mind?
My inbox is open - no forms disappearing into the void here.
- Just say hello Found a tutorial useful? Spotted a mistake? Tell me.
- Hire me for a project Have something custom in mind? Let's talk scope and timelines.
- Product support Bought something here? I'll help you get it running.
I usually reply within 1-2 business days.