DEV Community

Jerome Smadja
Jerome Smadja

Posted on

Focus Style for Keyboard Navigation Only

I had the idea of this post after reading this one Designing button focus states for better usability, if you haven't read it yet, go for it.

Accessibility vs UI

Well, I didn't really want to oppose a11y to UI as a fight but couldn't come up with a better name.

Sometimes, you might want to remove the default outline the browser will provide for free. When you do that, you certainly please your designers, but you're making it hard for people who rely on assistive technologies, or keyboard navigation.

With :focus-visible (Specs in Working Draft) you can make both sides happy. I still wouldn't recommend to remove the whole outline on :focus on every element of your app because it can still benefit and help some users.

Though, there are some cases where the focus ring might not be great in your UI, the ones I can think of would be clickable icons, e.g. arrows ← → for pagination, hamburger icon to open a menu.

Example of a click on a hamburger icon to open a menu which also shows the native outline focus ring

Also, in the case of a custom style <button> having a high border-radius, the outline won't follow the rounded corners, and will result in this:

button with native outline when focused

When clicking with a mouse or tap with your finger the added value of this focus ring is low (:hover and :active are probably better candidates), and it even adds useless noise to the UI. On the other hand, when tabbing through a website, users need to know that the focus is on the hamburger icon so that they can open the menu.

:focus-visible To The Rescue

The main difference between the two :focus and :focus-visible, is that you can use
:focus-visible for people not navigating with a pointing device (mouse, touch, etc...).
That means that we can target people relying on keyboard navigation.

As of April 2019, it's implemented in Chrome only and behind a flag. However the good news is that if we still want to use this, we still can by using a polyfill

If in your codebase, you can remove the outline : none that you had, install this polyfill, and update your CSS with this (as a first step)

  This will hide the focus indicator if the element receives focus via the mouse,
  but it will still show up on keyboard focus.
.js-focus-visible :focus:not(.focus-visible) {
  outline: none;
Enter fullscreen mode Exit fullscreen mode

This selector will filter out all the elements focused by a keyboard event, and won't apply in that case, so you can keep the default outline by default.

A little detail that I really like in this spec is that :focus-visible also applies in case the user focuses an <input> field either with a mouse or a keyboard. Why? The rule is simple, if an interactive element opens the keyboard (on a mobile) it should keep the focus ring. I completely agree with this, even on desktop having an indication on where your caret is, can only be a good thing.

A workmate showed me this great example on how to get crazy with focus for keyboard navigation, there even are animations based on the direction when you navigate:

So let's end this era of :focus { outline: none; } now that we have a reliable solution that works for everyone and can benefit to lots of people.


A good read on the intent behind focus-visible:


Top comments (2)

westbrook profile image
Westbrook Johnson

I agree that :focus-visible is cool and all, but if an element is clicked and takes focus because of that, the UI should explain that to the user. Otherwise, how does low visibility but high dexterity user know what will happen when then subsequently use the enter or space keys to interact with the page?

They need to know what will happen visually. I can get the "let's not make it ugly" approach, but we could do that even without :focus-visible, we just need to put some common sense after out outline: none; declarations...

Even if there is long term benefit in distinguishing between focus applied by the keyboard and focus applied by the mouse, we can't simply stop showing the focus that comes with the mouse, it's still happening and cause side effects. In cases where :focus-visible is used, we should be backing it up with a :focus state that is acceptable in these contexts. Then we can both appease picky designers and support users who deserve high-quality experiences when using our applications.

robmarshall profile image
Robert Marshall

I have been looking into this. Unfortunately the flag issue is a major barrier. The fact that extra JS is needed to make it work feels dirty. A solution that works quite well is a slight change of how the HTML and CSS is structured. This article should help anyone who needs it: