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.