Enhance a11y using :focus-visible pseudo-class
Tags: html, css, javascript, a11y
Published: 2021-05-09 13:13:56

Accessibility is one of the important aspects of the applications. Building the accessible applications will make sure your application will be used by all people(irrespective of their disabilities). According to [W3C](https://www.w3.org/standards/webdesign/accessibility),
> The Web is fundamentally designed to work for all people, whatever their hardware, software, language, location, or ability. When the Web meets this goal, it is accessible to people with a diverse range of hearing, movement, sight, and cognitive ability.


## Problem Statement
In order to make our web pages accessible, sometimes we need to explicitly set the [focus](https://www.youtube.com/watch?v=srLRSQg6Jgg) on some non-interactive elements(headings, table, main etc) so that it will be read out by [screen-readers](https://en.wikipedia.org/wiki/Screen_reader). But by doing so we might also annoy our regular users(users who will not use screen readers). Normal users can read the text easily. It would be good if there is a way to make the non-interactive elements focusable only for keyboard users or screen-readers.

## :focus-visible is the rescue
From MDN,
> The :focus-visible pseudo-class applies while an element matches the :focus pseudo-class and the UA (User Agent) determines via heuristics that the focus should be made evident on the element. (Many browsers show a “focus ring” by default in this case.)
This selector is useful to provide a different focus indicator based on the user’s input modality (mouse vs. keyboard).

I hope this will make sense now. The syntax to use it looks like:-
```
.element:focus-visible {
  /* Add some styles to get the user's attention */
}
```
With this, we can add styles to focus on keyboard input. But this is not enough for what we want. For mouse users, we need to remove the focus styles from non-interactive elements,
```
.element:focus {
  outline: none;
}
```
Now when the user tab on the element(make sure to add tabindex on non interactive elements), the element will get the focus but if we mouse click to the element, the element will not get the focus. You can check this very basic example.
{% codepen https://codepen.io/gauravsoni119/pen/OJpVmZa %}

Now we have the :focus-visible pseudo-class. But there is again an issue. This selector is supported by chrome, firefox, and edge but not by safari. But thanks to the [polyfill](https://github.com/WICG/focus-visible).
With this polyfill, it will be supported by all major browsers. But we need to slightly update the code.
```
/*
  This will hide the focus indicator if the element receives focus via the mouse,
  but it will still show up on keyboard focus.
*/
.element:focus:not(.focus-visible) {
  outline: none;
}

.element.focus-visible {
 /* Add some styles to get the user's attention */
}
```
You may have noticed that instead of using the pseudo-class, we are using the class `.focus-visible` that is provided by the polyfill. With this change, we are all set to use this browser feature.
## Learn more:-
* https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible
* https://css-tricks.com/keyboard-only-focus-styles/
* https://github.com/WICG/focus-visible