Focus Management
A native focus system built with :focus and :focus-visible. The custom properties define the indicator, while the browser decides when focus should be visible.
0 KB JavaScript
16 CSS lines
browser managed logic
native fallback
Preview
Generic focus ring behavior
Click meWith fallback on :focus, refined by :focus-visible
The code
CSS mechanisms
/* ----------------------------------------------------------------------------
* Focus Variables
* ---------------------------------------------------------------------------- */
/**
* Focus indicator defaults
*
* These custom properties define the default focus indicator used by the
* focus mechanisms below.
* Override them in a theme, component, or local scope to change the visual
* result without changing the focus behavior.
*/
:root {
--focus-width: 2px;
--focus-color: currentColor;
--focus-offset: 2px;
}
/* ----------------------------------------------------------------------------
* Focus Management
* ---------------------------------------------------------------------------- */
/**
* Focus fallback
*
* Ensures focused interactive elements have a visible indicator by default.
* In browsers that support `:focus-visible`, this fallback is refined below.
*/
:where(a[href], button, input, select, textarea, summary, [contenteditable]:not([contenteditable="false"]), [tabindex]:not([tabindex^="-"])):focus {
outline: var(--focus-width) solid var(--focus-color);
outline-offset: var(--focus-offset);
}
/**
* Removes the fallback indicator when the browser determines that focus
* visibility is not needed, for example after pointer interaction.
*/
:where(a[href], button, input, select, textarea, summary, [contenteditable]:not([contenteditable="false"]), [tabindex]:not([tabindex^="-"])):focus:not(:focus-visible) {
outline: none;
}
/**
* Visible focus
*
* Applies the focus indicator when the browser determines that focus should
* be visible, usually during keyboard or sequential navigation.
*/
:where(a[href], button, input, select, textarea, summary, [contenteditable]:not([contenteditable="false"]), [tabindex]:not([tabindex^="-"])):focus-visible {
outline: var(--focus-width) solid var(--focus-color);
outline-offset: var(--focus-offset);
}Benefits
-
01.
Accessible fallback
Focused interactive elements keep a visible indicator even before
:focus-visiblerefinement is applied. -
02.
Native behavior
The browser handles focus visibility heuristics across keyboard, pointer, touch, and script-driven focus changes.
-
03.
Theme friendly
The ring width, color, and offset are controlled through custom properties rather than duplicated in each rule.
-
04.
Easy overrides
The selector remains intentionally light, so components can replace or refine the focus style without fighting specificity.
Current limitations
-
01.
Browser logic
:focus-visibledepends on user-agent logic. Text inputs, script focus, and pointer focus may be treated differently across browsers. -
02.
Focusable markup
The rules only help elements that can receive focus. Disabled controls, links without
href, and unmanaged custom widgets still need correct markup. -
03.
Contrast responsibility
The mechanism exposes the indicator, but the chosen
--focus-color, width, and offset still need to remain visible in every theme.