:focus-visible

Avatar of Andy Adams
Andy Adams on (Updated on )

DigitalOcean provides cloud products for every stage of your journey. Get started with $200 in free credit!

The :focus-visible pseudo-class (also known as the “Focus-Indicated” pseudo-class) is a native CSS way to style elements that:

  1. Are in focus
  2. 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:

Outline added by the browser on :focus

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:

One of these is focused, but you can’t see it!

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:

:focus-visible makes focus visible!

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:

SituationDoes :focus-visible apply?
The user says they always want the focus to be visible via a settingYes
An element needs a keyboard to function (like text <inputs>)Yes
The user is navigating with a keyboardYes
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 elementYes
A script causes focus to move from a non-:focus-visible element to another elementNo

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

ChromeFirefoxIEEdgeSafari
864*No8615.4

Mobile / Tablet

Android ChromeAndroid FirefoxAndroidiOS Safari
12712712715.4

Additional information