Focus styles that respect every input method
The focus indicator—the ring or highlight that marks the currently focused element—is the keyboard user's stand-in for a mouse pointer. Losing it means losing your place on the page. Browser defaults may not fit your design, but when you restyle focus, the goal is always the same: make sure users can still tell where they are.
:focus for an unconditional indicator
The :focus pseudo-class applies to any element in focus, no matter the input device. You can make a <div> focusable with a tabindex attribute and style it directly:
div[tabindex="0"]:focus {
outline: 4px dashed orange;
}
With :focus, the element looks the same when focused by mouse, keyboard, or stylus. The catch is that browsers are inconsistent about when an element receives focus at all. A <button> styled with a custom :focus rule will show that style when clicked in Chrome on macOS, but not in Safari on macOS, where clicking the button does not put it in focus.
button:focus {
outline: 4px dashed orange;
}
That means you need to test your focus styles across browsers and input methods. If focus behavior differs, your carefully-crafted indicator may never appear for some users.
:focus-visible when the indicator actually helps
The :focus-visible pseudo-class matches only when the browser's heuristics decide a focus indicator is genuinely useful. Typically, that is when the most recent interaction came from the keyboard and the key press did not include a meta, ALT, OPTION, or CONTROL key.
button:focus-visible {
outline: 4px dashed orange;
}
With :focus-visible, a control can show a ring for keyboard navigation while staying clean for mouse clicks.
:focus-within for highlighting a whole region
The :focus-within pseudo-class applies to an element when it, or any child element, receives focus. This makes it easy to draw attention to a region, such as a form, when interaction moves inside it:
form:focus-within {
background: #ffecb3;
}
With this rule, the form gets its highlight both when the form itself is selected and when any of its radio buttons are selected.
Deciding when to show a ring
A quick heuristic helps settle whether a control should always show a focus indicator or only under certain conditions: imagine clicking the control on a mobile device. Would a keyboard appear?
- If yes, always show a focus indicator regardless of input device. Text inputs like
<input type="text">need this because the user must type into them no matter how focus arrived. - If no, a selective indicator is fine. Clicking a
<button>with a mouse or a touchscreen completes the action, so the ring adds little. For keyboard users, however, the ring is essential—it tells them whether ENTER or SPACE will activate the control.
Why outline: none is a dead end
Browser rules for when the default ring appears are genuinely confusing. Merely giving an element a tabindex or restyling a <button> can change the default behavior.
Removing the indicator outright is tempting but short-sighted for users who need it:
/* Don't do this!!! */
:focus {
outline: none;
}
A more robust approach combines :focus with the :focus-visible polyfill. The polyfill lets you apply the selective behavior consistently, even where native support is missing:
/*
This hides the focus indicator if the element receives focus with a
mouse, but it still shows up on keyboard focus.
*/
.js-focus-visible :focus:not(.focus-visible) {
outline: none;
}
/*
Optionally: Define a strong focus indicator for keyboard focus.
If you choose to skip this step, then the browser's default focus
indicator will be displayed instead.
*/
.js-focus-visible .focus-visible {
...
}
Used this way, you get a clear indicator for keyboard users and avoid a permanent ring that annoys mouse users.



