With :focus-visible, you can have focus styles when it makes sense

Focus outlines are a great way to improve accessibility. They are traditionally set with the :focus pseudo class. That still works, but with :focus-visible we have a new way to only show focus styles when they make sense. How does that work?

The :focus-visible pseudo class has been in the works for over seven years and we recently got to a situation where it is now in stable versions of all modern browsers: Chrome/Edge (from 86), Safari (from 15.4) and Firefox (from 85).

What is focus-visible anyway?

The thing is, :focus-visible isn't a “indicate focus only to keyboard users” pseudo class, it is “indicate focus when the browser thinks it's right, based on some heuristics”.

“When it is right”, what does that even mean? Well, it has to do with when browsers decide to show their default outline. For example, most browsers show an outline when you press a button with a keyboard, but they don't when you click a button with a mouse. In other words, focus styles in browsers only show sometimes, in specific cases. The :focus-visible pseudo class is meant to match those cases.

This makes :focus-visible very different from :focus, which matches whatever the currently focused element is, regardless of whether it makes sense to highlight it or not. That's why you might see the styles you applied through :focus even if you click on something with a mouse, a behavior that could leave users confused and causes some developers to turn off highlights completely (but friends don't let friends do this, you would not do cursor: none either).

From CSS Selectors, Level 4, 9.4:

[:focus-visible allows] authors to change the appearance of the focus indicator without changing when a focus indicator appears.

So it lets you target the cases where browsers would normally apply focus styles, and, importantly, it excludes the cases where browsers don't paint their default outline, for instance when the user clicks on a thing with a mouse.

Consequently, browsers apply :focus-visible styles to more non-mouse cases than just keyboard users. Which cases are they?
There are lots of devices that are keyboard-like, as Eric Bailey explains in Focusing on focus styles:

Wands, sticks, switches, sip and puff devices, voice recognition, and eye tracking technology can all create input in a digital system. These devices will identify a content area and activate it. This is similar to how you can hit the tab key on a keyboard and the next cell in a spreadsheet will be highlighted, indicating that it has been moved to and is ready to be edited.

Some of these technologies present themselves as keyboards (like braille displays), they could fall under the focus-visible umbrella, others (like voice control) are more mouse-like and may not trigger these heuristics. Some assistive technologies also come with their own highlighting, like VoiceOver on iOS and the macOS Switch Control UI.

Pointers vs non-pointers

An example (non-normative) in the specification for :focus-visible suggests that :focus-visible should apply to interactions “via keyboard or some other non-pointing device”. This made me wonder what the exact difference is between a pointing device and a non-pointing device. The Pointer Events specification has some answers (thanks Bramus!).

Basically, there are input methods that can do mouse clicks and input methods that aren't really a mouse but can simulate mouse clicks, like touchscreens and pen input. Pointer Events tries to abstract all of those input methods into a new concept called “pointer”.

The definition of a pointer in that specification:

A hardware agnostic representation of input devices that can target a specific coordinate (or set of coordinates) on a screen, such as a mouse, pen, or touch contact.

I haven't found a definition for non-pointer devices, like keyboards, but my best bet at a description would be: non-pointing devices are devices that let you step between the different interactive parts of a user interface. Others also call it “sequential navigation”.

Examples of non-pointer or sequential navigation:

  • TAB/shift + TAB or arrow keys when you use a keyboard
  • gestures when using VoiceOver on iOS, you flick between the different elements
  • item mode in switch control (see Milan Patel's switch control demo, item mode demo at 3:59, note switch control has a pointer mode too, which Milan mentions he finds easier)

For the sake of completeless, there is also the concept of “spatial navigation”. This is similar to sequential navigation, but you don't just go back and forward—you can go up and down too, like when you select something to watch on a streaming service on your TV.

Input methods differ and overlap

Even with this distinction between pointing devices and non-pointing devices, none of this is very binary. There are users who always use a keyboard or users who never do, but most people will be somewhere in between. They could be switching between a mouse and a keyboard in one browser session, or use devices that allow for multiple input methods. Someone could connect a bluetooth keyboard to until-then touch-only tablet. Switch Control in iOS has both a point mode and an item mode.

And even if you could detect every type of device, people are not the same. You likely have users who use a mouse, but still benefit from seeing what currently has focus: users with low vision and users with cognitive disabilities.

In a comment discussing focus-visible, Jonathan Avila shares how he switches between modalities:

I often switch between input modalities such as by clicking and dragging off a button to set focus somewhere then use the keyboard to navigate from there. I may click on a radio button and then use arrows to select other radio buttons. I will switch between touch, mouse, keyboard, and many other settings such as large text, screen reader, or zoom depending on the situation.

The cool thing about :focus-visible is that it allows browsers to be smart about when to show focus styles. Browsers won't just hop into visible focus mode when you press any key, it takes things like using command/control + key combos into account, as Alice Boxhall explains in a recent Igalia Chats episode. The heuristics develop over time, too.

Ok, so what now?

This post turned out a little longer than I intended, but what I've tried to capture is what I started with: that and why :focus-visible is more than a way to show focus styles just to keyboard users.

If you want to hear more about :focus-visible from people who worked on this, the aforementioned recent Igalia Chats episode with Brian Kardell, Alice Boxhall and Rob Dodson covers some of the history and evolution.

Thanks to Job van Achterberg, Roel Van Gils, Eric Eggert and Patrick H. Lauke for their feedback and input on earlier drafts.

Comments, likes & shares (66)

@hdv explains the benefits of using :focus-visible instead of :focus, and why it's not only useful for keyboard users hidde.blog/focus-visible-…
🔴 With :focus-visible, you can have focus styles when it makes sense by Hidde de Vries @hdv #css #webdev #accessibility #focusvisible hidde.blog/focus-visible-…
Just read: "With :focus-visible, you can have focus styles when it makes sense" hidde.blog/focus-visible-…
Thanks for publishing an article on on :focus-visible right when I needed it @hdv! hidde.blog/focus-visible-…
“The thing is, :focus-visible isn't a ‘indicate focus only to keyboard users’ pseudo class, it is ‘indicate focus when the browser thinks it's right, based on some heuristics’.” A detailed look at `:focus-visible` by @hdv 👏 hidde.blog/focus-visible-…
With “:focus-visible”, You Can Have Focus Styles When It Makes Sense, by @hdv: hidde.blog/focus-visible-…
📝 With :focus-visible, you can have focus styles when it makes sense 🔗 hidde.blog/focus-visible-… #html #css #javascript #webdev
With :focus-visible, you can have focus styles when it makes sense hidde.blog/focus-visible-… #css #webdev #a11y
With :focus-visible, you can have focus styles when it makes sense hidde.blog/focus-visible-…
With :focus-visible, you can have focus styles when it makes sense 🙂 hidde.blog/focus-visible-…
With :focus-visible, you can have focus styles when it makes sense 👀 hidde.blog/focus-visible-… by @hdv
With :focus-visible, you can have focus styles when it makes sense, by @hdv hidde.blog/focus-visible-…
You've been able to use :focus to create great outlines on important links, but with :focus-visible, you can take it even further, as @hdv shows. hidde.blog/focus-visible-…
If I’m reading @hdv correctly, :focus-visible won’t work because you’re setting focus on a non-heuristic element. Can you add a JS class on focus that targets selector div[tabindex=“-1”] :focus ? It’s not quite what you’re asking, but maybe a step? hidde.blog/focus-visible-…