The :focus-visible
pseudo-class (also known as the “Focus-Indicated” pseudo-class) is a native CSS way to style elements that:
- Are in focus
- Need a visible indicator to show focus (more on this later)
:focus-visible
is used similarly to :focus
: to bring attention to the element that currently has the focus.
.element:focus-visible {
background-color: pink; /* Something to get the user's attention */
}
:focus-visible
is part of the CSS4 Selectors working draft. Prior to adoption, Mozilla introduced the :-moz-focusring
pseudo-class to bring the functionality to Firefox ahead of a formal specification.
Why do we need :focus-visible?
Doesn’t :focus
do this already? Yes, but there are problems. The clearest illustration is a button that fires some JavaScript. Imagine an image carousel with buttons to swap between images. Let’s say you’ve added a tabindex
to the buttons so they can be selected with a keyboard, but when you go to test the carousel with your mouse, you see this outline around your gorgeous button:
Not that you would want to do this (for accessibility concerns), but how do you get rid of it? By setting the :focus
pseudo-class:
.next-image-button:focus {
outline: none;
}
Now your button looks great when it is in focus, but what happens when a user tabs to your button without a mouse but a keyboard instead? They can’t see where they’ve tabbed! That’s a problem because now there’s no way to tell which button is focused for keyboard actions:
Is there a way to remove the blue focus outline but still show a focus that’s more in line with the site design? Sure, you can have your cake and eat it too, thanks to :focus-visible
!
:focus-visible
only applies when you actually want a visual indicator to help the user see where the focus is. In other words, it can’t hide the outline like :focus
can. (Well, it could by blending it into the design, but whatever.) The two have to be used together in that sense. Let’s add one to our button:
.next-image-button:focus {
outline: none;
}
.next-image-button:focus-visible {
outline: 3px solid blanchedalmond; /* That'll show 'em */
}
Now, when the keyboard is used to tab to the button, there will be a visual indication of the focus:
How do browsers determine when something is :focus-visible?
Browsers are given a bit of leeway to determine when this pseudo-selector should be applied to a given element using their own heuristics. First, let’s look at the CSS4 working draft, and then we’ll try to break it down. From the specifications:
- If a user has expressed a preference (such as via a system preference or a browser setting) to always see a visible focus indicator, the user agent should honor this by having :focus-visible always match on the active element, regardless of any other factors. (Another option may be for the user agent to show its own focus indicator regardless of author styles.)
- Any element which supports keyboard input (such as an input element, or any other element which may trigger a virtual keyboard to be shown on focus if a physical keyboard is not present) should always match :focus-visible when focused.
- If the user interacts with the page via the keyboard, the currently focused element should match :focus-visible (i.e. keyboard usage may change whether this pseudo-class matches even if it doesn’t affect :focus).
- If the user interacts with the page via a pointing device, such that the focus is moved to a new element which does not support user input, the newly focused element should not match :focus-visible.
- If the active element matches :focus-visible, and a script causes focus to move elsewhere, the newly focused element should match :focus-visible.
- Conversely, if the active element does not match :focus-visible, and a script causes focus to move elsewhere, the newly focused element should not match :focus-visible.
If that’s a little abstract, here’s an interpretation:
Situation | Does :focus-visible apply? |
---|---|
The user says they always want the focus to be visible via a setting | Yes |
An element needs a keyboard to function (like text <inputs>) | Yes |
The user is navigating with a keyboard | Yes |
The user is navigating with a pointing device (like a mouse or finger on a touchscreen) | No |
A script causes focus to move from a :focus-visible element to another element | Yes |
A script causes focus to move from a non-:focus-visible element to another element | No |
It bears repeating: These are guidelines, and browsers will be able to make their own determination about what is selected by :focus-visible
. We can expect that the obvious case of keyboard navigation will be handled in a predictable way, but the browsers have the ability to make the determination themselves, like any other feature.
Browser support
This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.
Desktop
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
86 | 4* | No | 86 | 15.4 |
Mobile / Tablet
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
123 | 124 | 123 | 15.4 |
Additional information
- CSS Selectors 4 Specification
- Bugzilla Ticket #1445482
- WebKit Ticket #185859
- WICG
:focus-visible
polyfill explanation - Patrick H. Lauke description & use of
:focus-visible
- Focusing on Focus States