Focus styles and programmatic focus
:focus
and :focus-visible
CSS selectors
For users navigating via a keyboard, its important to keep focus styles prominent and clearly noticable so that the user can follow where they are on the page. Styles defined with the :focus
pseudo-class are applied whenever an element has focus. Clicking a button or an input with a mouse focuses that element, so mouse users will also see :focus
styles. For mouse users, these styles are unecessary, may be confusing and visually distracting, and might be deemed ugly. For keyboard users, they are a necessity.
Styles set using the :focus-visible
pseudo-class will appear when a user is navigating using a keyboard, but will not be applied by mouse interactions*.
:is(select, button, input):focus {
outline: none;
}
:is(select, button, input):focus-visible {
outline: solid 2px blue;
}
*Which styles get applied depends on the element. A button, link, checkbox, radio, range, color or file input, or an element with a tabindex of 0, will only show focus-visible
styles when the user has focused the element using a keyboard. Any input with a text field (e.g. email, password or number input, textarea) will show both focus
and focus-visible
styles regardless of how the user focused the element. A select will show both focus
and focus-visible
styles for mouse-clicks in Chrome, but only focus
styles in Firefox 🤷‍♂️.
Programmatic focus styles
What happens when you focus an element using JavaScript?
:focus
styles are always applied when an element is focused using JavaScript. Whether :focus-visible
styles are applied depends on the context.
In the following example, clicking button2 with a mouse will focus button1 but won’t show button1’s :focus-visible
styles. If you use a keyboard to press button2, button1’s :focus-visible
styles will be shown.
button2.addEventListener('click', function() {
button1.focus();
});
The above example uses the following (somewhat lurid) styles:
button:focus {
outline: dashed 3px pink;
background-color: yellow;
}
button:focus-visible {
outline: solid 2px blue;
border-radius: 3px;
}
The HTML spec is somewhat vague about what’s meant to happen. If, “in an implementation-defined way the user agent determines it would be best to do so, then indicate focus”. It’s left up to individual browsers to decide what to do, which leaves scope for inconsistencies. Current default behaviours shouldn’t be relied upon as any browser could opt to change its behaviour in the future.
Whether :focus-visible
styles are applied can be controlled via a focusVisible
boolean option:
button1.focus({focusVisible: false});
Set focusVisible
to false and :focus-visible
styles won’t be applied, set it to true and they will be.
Browser support
The focusVisible
option for the .focus()
method is supported in Firefox and Safari 18.4.