Focus-visible: The Unsung Hero

You find a lot of edge cases building UI's. Accessibly opening a modal, scroll-lock, strange focus states, etc..., and the solution to those edge cases aren't always apparent.

Well, I just found one that I already knew the answer too but forgot- so I'm writing this article to cement in my head and share the knowledge with all of you!

What is :focus-visible?

According the MDN, :focus-visible is a pseudo-class that applies while an element matches the :focus pseudo-class and determines vie heuristics that the focus should be made evident on the element.

In human speak? The browser determines if the :focus came from a click event or keyboard event and applies the styles accordingly. In most cases, that means that it won't apply the :focus styles from a click event.

That means that you give your keyboard users the focus styles they may want/need/desire without having a potentially jarring user experience for your mouse users.

What was the problem?

The issue I ran into was that I needed an element to have focus styles for accessibility's sake- but I didn't need some of those focus styles to apply when it was clicked.

To be even more clear, the hover and focus styles are the same. I wanted the hover styles to apply on hover but I didn't need the focus styles to linger after the element was clicked.

.logo-link:hover,
.logo-link:focus,
.initial-display {
  .logo-text {
    @apply translate-y-0;
  }
  .logo-type {
    @apply opacity-100;
  }
  .logo-tagline {
    @apply opacity-80;
  }
}

You can see there wasn't much to the styles, but the client was adamant about this user experience.

The Solution

The answer to this problem was only 8 characters long!

Adding -visible to the end of the :focus pseudo-class was all it took.

.logo-link:hover,
.logo-link:focus-visible,
.initial-display {
  .logo-text {
    @apply translate-y-0;
  }
  .logo-type {
    @apply opacity-100;
  }
  .logo-tagline {
    @apply opacity-80;
  }
}

Now I can see clearly

I was absolutely ready to go town with javascript by using a click event to force .blur() and be as strong-handed as I needed to be to make it work.

I mentioned earlier that I knew this answer, but that I didn't come to this solution on my own. A co-worker suggested :focus-visible and it immediately came rushing back.

I ran into this exact problem a couple of months and the fix was the exact same thing!

Can I Use

There is one caveat to :focus-visible, and that is that it doesn't have full browser support yet.

You can see in the image below that it has decent coverage of around 70%, but it has 0 support in Safari, so you'll have to be sure to cover all of your bases and add in some polyfills.

22