:focus-visible.
Keyboard focus without the mouse.
For years, developers did one of two things: left the ugly default focus outline on every element (keyboard users loved it, mouse users hated it), or set outline: none everywhere (mouse users loved it, keyboard users couldn't navigate). :focus-visible solves both — it shows focus styles only when the user is navigating by keyboard. One pseudo-class, one best practice, one accessibility problem solved.
Short answer: Use :focus-visible instead of :focus for interactive elements. Style it with outline: 2px solid #2965f1; outline-offset: 2px — a visible, on-brand focus ring that only appears for keyboard users. Never write outline: none without a replacement — it breaks keyboard navigation. :focus-visible is supported in all modern browsers since 2022. If you must support older browsers, use :focus as a fallback.
✅ Before and after
The old way and the fix.
/* ❌ The accessibility failure /
button,
a,
input {
outline: none; / removes focus indicator */
}
/* No replacement — keyboard users see nothing. /
What happens: 1. Keyboard user presses Tab. 2. Focus moves to the next focusable element. 3. Nothing changes visually — no outline, no indicator, no feedback. 4. User doesn't know where focus is. 5. User can't use the page. This is a WCAG 2.4.7 failure (Focus Visible). It's one of the most common accessibility bugs on the web, and one of the most easily fixed. Never write outline: none on focusable elements without providing a replacement. If a designer insists "the blue outline is ugly," the answer is not to remove it — it's to replace it with a better one.
/* ✅ The modern fix /
button:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
outline: 2px solid #2965f1;
outline-offset: 2px;
border-radius: 2px; / optional, rounds the outline */
}
/* Keyboard focus: visible outline /
/ Mouse click: no outline */
/* Modern single-line version with :where() /
:where(button, a, input, select, textarea):focus-visible {
outline: 2px solid #2965f1;
outline-offset: 2px;
}
Keyboard user tabs to a button: ┌──────────────────────┐ │ ┏━━━━━━━━━━━━━━━━━━┓ │ ← 2px blue outline │ ┃ Click me ┃ │ offset by 2px │ ┗━━━━━━━━━━━━━━━━━━┛ │ └──────────────────────┘ Mouse user clicks the button: ┌──────────────────────┐ │ ┌──────────────────┐ │ ← no outline │ │ Click me │ │ (but hover styles │ └──────────────────┘ │ may still apply) └──────────────────────┘ ✅ Keyboard users see where focus is. ✅ Mouse users don't see the outline. ✅ WCAG 2.4.7 satisfied. ✅ Designers and users both happy. outline-offset: 2px pushes the outline 2px outside the element — makes it visible without being clipped by the border. For tight layouts, use 2px; for more breathing room, 3-4px. outline-color should match your brand. Use the primary accent — #2965f1 here. Don't use box-shadow instead — it can be clipped by overflow: hidden and doesn't follow the border radius the way outline does (since 2022).
✅ The fallback for older browsers
What to do if :focus-visible isn't supported.
/* Fallback — :focus works everywhere */
button:focus,
a:focus,
input:focus {
outline: 2px solid #2965f1;
outline-offset: 2px;
}
/* Then override with :focus-visible for browsers
that support it */
@supports selector(:focus-visible) {
button:focus,
a:focus,
input:focus {
outline: none;
}
button:focus-visible,
a:focus-visible,
input:focus-visible {
outline: 2px solid #2965f1;
outline-offset: 2px;
}
}
How it works: Browser without :focus-visible support: Applies the first rule (button:focus). Skips the @supports block. → Shows outline on both keyboard AND mouse focus. → Safe fallback. Browser with :focus-visible support: Applies the first rule. Also applies the @supports block. → :focus outline is set to none. → :focus-visible outline is set. → Shows outline only on keyboard focus. → Modern behavior. ✅ Same CSS works in both cases. ✅ Accessibility preserved everywhere. ✅ Progressive enhancement, no JS. @supports selector(:focus-visible) is a feature query — it checks whether the browser supports the :focus-visible pseudo-class. For most sites in 2026, all modern browsers support :focus-visible (Safari 15.4+, 2022). The fallback is for IE, older Safari, or very old Android browsers.
Don't use outline: none and expect :focus-visible to fix it. If you write button:focus {'{ outline: none; }'} and then button:focus-visible {'{ outline: 2px solid #2965f1; }'}, mouse clicks get no focus indicator (fine) and keyboard focus gets the outline (fine) — but the browser's default focus outline is still applied between the two rules for browsers that don't support :focus-visible. Worse, some browsers fire :focus-visible on mouse click too, so the outline appears when you don't want it. The correct pattern is: never write outline: none. Write only :focus-visible with an outline. Remove the default, replace with yours, done.
⚠️ Common mistakes
The focus-visible mistakes that cost real time.
outline: none without replacementBreaks keyboard navigation — WCAG failure. Fix: :focus-visible with a clear outline.
:focus everywhereHarsh outline on mouse clicks. Fix: :focus-visible.
box-shadow instead of outlineClipped by overflow, doesn't follow border-radius. Fix: outline with outline-offset.
Buttons, links, inputs — all need visible focus. Fix: cover all interactive elements.
tabindexDivs with ARIA roles don't get focus. Fix: add tabindex="0" or use native elements.
:focus-visible on selectDropdowns are focusable too. Fix: include them in the selector.
The outline touches the element — hard to see on borders. Fix: outline-offset: 2px.
Some bugs only show on mouse click. Fix: test both inputs, and with a screen reader.
❓ Frequently asked
What is :focus-visible?
focus-visible is a CSS pseudo-class that matches when an element has focus AND the user is interacting via keyboard. It does not match on mouse clicks. This solves the UX problem of users wanting focus outlines when tabbing but not when clicking. With :focus-visible, you can show the outline for keyboard users and hide it for mouse users — without compromising accessibility.
What is the difference between :focus and :focus-visible?
:focus matches whenever an element has focus — mouse click, keyboard tab, or JavaScript focus() call. :focus-visible matches only when the focus is 'user-visible' — typically keyboard interaction. Click a button, :focus matches, :focus-visible usually does not. Tab to a button, both match. This lets you show focus styles for keyboard users without the outline appearing when a mouse user clicks.
Why is removing the focus outline bad?
It breaks keyboard navigation. Users who navigate by Tab need to see where focus is — the outline is their cursor. outline: none removes the only visual indicator, so keyboard users have no idea which element is focused. This is a WCAG 2.4.7 failure. The fix is not to remove the outline — it's to replace it with a better one using :focus-visible.
Does :focus-visible work on all elements?
focus-visible works on any element that can receive focus — buttons, links, inputs, select, textarea, elements with tabindex. It's most useful on interactive elements that are clicked as well as tabbed to. For elements that only receive focus via keyboard, you can style :focus directly — but focus-visible is still more precise.
Why does focus-visible not show on my element?
Three common causes. (1) The element is not focusable. (2) The focus came from a mouse click — focus-visible intentionally hides in this case. (3) Another rule overrides focus-visible. Also check that the element actually receives focus — some elements need tabindex to be focusable at all.
Can I combine :focus-visible with :focus?
Yes, but it's usually unnecessary. For most cases, use :focus-visible alone. If you want a different style for keyboard vs mouse focus, you can use both. The main pitfall: don't write :focus { outline: none } and rely only on :focus-visible — that leaves mouse clicks with no visible focus at all, and some browsers still fire :focus.