CSS :focus-visible Pseudo-Class
The CSS :focus-visible pseudo-class selects a focused element when the browser determines that a visible focus indicator is appropriate. It is commonly used to highlight buttons, links, form fields, and other interactive elements during keyboard navigation without displaying the same focus styling after every mouse click.
:focus-visible Syntax
:focus-visible {
property: value;
}
The :focus-visible pseudo-class does not accept arguments. It can be combined with element selectors, class selectors, and other pseudo-classes.
Example Syntax
button:focus-visible {
outline: 3px solid #0088cc;
outline-offset: 3px;
}
This rule displays a blue outline when a focused button matches :focus-visible.
The :focus-visible pseudo-class has a specificity of 0-1-0. Combined with the button type selector, its specificity becomes 0-1-1.
Targeting Multiple Elements
a:focus-visible,
button:focus-visible,
input:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 2px;
}
This rule provides a consistent focus indicator for links, buttons, and input elements whenever they match :focus-visible.
How :focus-visible Works
The :focus-visible pseudo-class matches an element that has focus when the browser determines that a focus indicator should be visible.
Browsers use heuristics to make this decision. These heuristics can consider the input method, the type of focused element, and whether the previously focused element matched :focus-visible.
Keyboard navigation commonly causes interactive elements to match :focus-visible. However, the pseudo-class is not limited to keyboard focus.
Keyboard Navigation
a:focus-visible {
outline: 3px solid #0088cc;
outline-offset: 3px;
}
When a user navigates to a link using the Tab key, the link normally matches :focus-visible and displays the outline.
Pointer Interaction
button:focus-visible {
outline: 3px solid #0088cc;
}
Clicking a button with a mouse may give it focus without causing it to match :focus-visible. This helps avoid displaying keyboard-style focus indicators after ordinary pointer interactions.
Text Input Controls
input:focus-visible {
outline: 3px solid #0088cc;
background-color: #eaf6fc;
}
A text input commonly matches :focus-visible even when clicked with a mouse because it requires keyboard input for text entry.
Important: The browser determines when :focus-visible matches. It should not be treated as an exact equivalent of keyboard-only focus.
Matching Behavior
The following table describes common situations in which an element may match :focus-visible.
Expected Matching Behavior
| Condition | Typical Behavior |
|---|---|
| Button focused using Tab | Usually matches :focus-visible. |
| Link focused using Tab | Usually matches :focus-visible. |
| Button clicked with a mouse | May have focus without matching :focus-visible. |
| Text input clicked with a mouse | Usually matches :focus-visible. |
| Textarea receiving focus | Usually matches :focus-visible. |
| Element that does not have focus | Does not match :focus-visible. |
| Element focused through JavaScript | Depends on browser heuristics and the previous focus state. |
These behaviors are guidelines rather than fixed rules for every browser. User preferences and browser-specific focus handling can also affect matching.
Programmatic Focus
document.querySelector("#search").focus();
This moves focus to the element with the ID search. Whether it matches :focus-visible depends on the browser's focus-visible heuristics.
Focus and Hover Are Different
button:hover {
background-color: #006699;
}
button:focus-visible {
outline: 3px solid #0088cc;
}
The :hover pseudo-class responds to pointer positioning, while :focus-visible responds to the focused state and the browser's focus-indicator decision.
:focus-visible vs. :focus
Both pseudo-classes select focused elements, but they differ in when their styles apply.
Comparison
| Pseudo-Class | Matching Behavior |
|---|---|
:focus |
Matches an element whenever it has focus. |
:focus-visible |
Matches a focused element when the browser determines that a visible focus indicator is appropriate. |
Example
button:focus {
background-color: #eaf6fc;
}
button:focus-visible {
outline: 3px solid #0088cc;
outline-offset: 2px;
}
The background changes whenever the button has focus. The outline appears only when the button also matches :focus-visible.
Using :focus Without :focus-visible
input:focus {
border-color: #0088cc;
}
This rule changes the border color whenever the input receives focus, regardless of the input method.
Using :focus-visible for Focus Indicators
button:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 3px;
}
This rule provides a visible focus indicator when the browser determines it is needed.
Use :focus for styles that should apply whenever an element is focused. Use :focus-visible for focus indicators that follow browser heuristics.
:focus-visible vs. :focus-within
The :focus-within pseudo-class matches an element when it or one of its descendants has focus. Unlike :focus-visible, it does not depend on whether a focus indicator is appropriate.
Comparison
| Pseudo-Class | Matching Behavior |
|---|---|
:focus-visible |
Matches a focused element when a focus indicator is appropriate. |
:focus-within |
Matches an element when it or a descendant has focus. |
HTML
<div class="form-group">
<label for="username">Username:</label>
<input type="text" id="username">
</div>
CSS
.form-group:focus-within {
background-color: #eaf6fc;
}
.form-group input:focus-visible {
outline: 3px solid #0088cc;
}
When the input receives focus, the surrounding form group changes background color. The input receives an outline when it also matches :focus-visible.
Styling a Parent Based on Focus Visibility
.form-group:has(input:focus-visible) {
border-color: #0088cc;
}
The :has() pseudo-class allows a parent container to match when a descendant input matches :focus-visible.
Practical Uses
The :focus-visible pseudo-class helps create clear focus indicators without applying identical focus styling after every pointer interaction.
Keyboard-Friendly Navigation Links
.main-nav a:focus-visible {
outline: 3px solid #0088cc;
outline-offset: 4px;
text-decoration: underline;
}
This rule makes navigation links easier to identify during keyboard navigation.
Accessible Button Focus
button:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 3px;
}
The button displays a visible outline when it matches :focus-visible.
Highlighting Text Inputs
input:focus-visible,
textarea:focus-visible {
outline: 3px solid #0088cc;
outline-offset: 2px;
background-color: #eaf6fc;
}
These rules highlight text-entry controls when the browser considers a focus indicator appropriate.
Custom Checkbox Focus
input[type="checkbox"]:focus-visible {
outline: 3px solid #0088cc;
outline-offset: 3px;
}
The checkbox receives a visible outline during appropriate focus interactions. The checked state remains independent of focus.
Styling Focused Form Controls
.form-control:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 2px;
border-color: #005fcc;
}
This rule provides consistent focus styling for elements using the form-control class.
Focus Styles with Rounded Buttons
.rounded-button {
border-radius: 8px;
}
.rounded-button:focus-visible {
outline: 3px solid #0088cc;
outline-offset: 3px;
}
The outline follows the button's rounded shape and remains separated from its border.
Accessibility
Visible focus indicators help keyboard users identify which interactive element is currently active for keyboard input. They are an important part of accessible navigation.
The :focus-visible pseudo-class allows browsers to account for different input methods while preserving focus indicators when they are needed.
Recommended Focus Indicator
:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 3px;
}
This rule applies a visible outline to any element matching :focus-visible.
Avoid Removing All Focus Indicators
:focus {
outline: none;
}
This rule removes the browser's default focus outline and can make keyboard navigation difficult when no replacement indicator is provided.
Using a Focus-Visible Replacement
button:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 3px;
}
This approach customizes the focus indicator without removing it from keyboard navigation.
Removing Unnecessary Pointer Focus Outlines
button:focus:not(:focus-visible) {
outline: none;
}
button:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 3px;
}
This pattern removes the outline from focused buttons that do not match :focus-visible, while preserving an outline when the browser determines one is appropriate.
Use this pattern carefully. A focus indicator should remain visible whenever users need it, including when browser preferences require one.
Accessibility Tip: Test focus styles using Tab and Shift+Tab. Make sure the indicator is clearly visible against surrounding colors and is not hidden behind other page elements.
Best Practices
- Use
:focus-visibleto display focus indicators when the browser determines they are appropriate. - Do not assume
:focus-visiblematches only during keyboard navigation. - Remember that text inputs may match
:focus-visibleafter mouse clicks. - Use
:focuswhen styling should apply whenever an element has focus. - Use
:focus-withinwhen a parent should respond to a focused descendant. - Use clearly visible outlines with sufficient contrast against surrounding content.
- Use
outline-offsetto separate the focus indicator from the element. - Avoid removing default focus outlines without providing an accessible replacement.
- Prefer native interactive elements that support keyboard navigation automatically.
- Do not rely only on color changes to indicate focus.
- Test keyboard, mouse, touch, and programmatic focus behavior where appropriate.
- Remember that browser heuristics and user preferences can affect matching.
:focus-visible Example
This example demonstrates the difference between :focus and :focus-visible. Use the Tab key to move between controls, then compare the results with mouse clicks. The buttons display a blue outline when they match :focus-visible, while the text input commonly displays its focus outline with either input method.
HTML
<div class="focus-visible-demo">
<p>Use Tab or click the controls below.</p>
<button type="button">First Button</button>
<button type="button">Second Button</button>
<label for="demo-input">Text Input:</label>
<input type="text" id="demo-input" placeholder="Enter text">
</div>
CSS
.focus-visible-demo button {
padding: 8px 12px;
margin: 0 5px 10px 0;
border: 1px solid #006699;
background-color: #0088cc;
color: #fff;
cursor: pointer;
}
.focus-visible-demo button:focus {
background-color: #006699;
}
.focus-visible-demo button:focus-visible {
outline: 3px solid #27ae60;
outline-offset: 3px;
}
.focus-visible-demo label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.focus-visible-demo input {
display: block;
width: 100%;
box-sizing: border-box;
padding: 8px;
border: 1px solid #999;
font: inherit;
}
.focus-visible-demo input:focus-visible {
outline: 3px solid #0088cc;
outline-offset: 2px;
background-color: #eaf6fc;
}
Browser Result
Use Tab to focus each button and observe the green outline. Clicking a button may change its background without showing that outline. The text input generally receives a blue outline when focused using either the keyboard or mouse.
Note: Focus behavior can differ between browsers and operating systems. Click inside the example before using Tab if keyboard focus is initially outside the iframe.
Browser Support
Baseline: Widely available. The :focus-visible pseudo-class is supported in modern versions of Chrome, Edge, Firefox, Safari, and other major browsers.
Support became widely available across major browsers in 2022. Older browser versions may not recognize the selector.
Browsers may apply slightly different focus-visible heuristics, so the exact matching behavior can vary even when the selector is supported.
For current compatibility information, visit Can I Use? .
Specifications
The :focus-visible pseudo-class is defined in Selectors Level 4 as a user-action pseudo-class. The specification describes the conditions under which a browser may determine that a focus indicator should be visible.
