Problem
Default :focus behavior is bad.
Focus rings can be confusing for users with pointing devices (e.g. mouse, touch screen) because it implies a sense of persistence when showing up right after a click or a touch event.
Designers then need to make the :focus state obvious enough for keyboard users but at the same time, keeping it unobtrusive for mouse users.
This usually involves a compromise. Some, even remove the default focusing behavior entirely which is terrible for accessibility.
Removing the
:focusoutline is like removing the wheelchair ramp from a school because it doesn’t fit in with the aesthetic.
David Gilbertson
Solution
Ideally, the focus ring should only show up when the user intends to use the keyboard.
We need a better default browser behavior. In cases like this, it’s always a good idea to check if there are existing or upcoming browser specifications that solve our problem.
Luckily for us, there’s one: Introducing :focus-visible 🎉.
Specification
TLDR; :focus-visible is the keyboard-only version of :focus.
Also, the W3C proposal mentions that :focus-visible should be preferred over :focus except on elements that expect a keyboard input (e.g. text field, contenteditable).
Browser Support
At the time of writing, only Firefox supports :focus-visible natively. But the good news is that there’s an excellent polyfill available for us. Here’s a demo if you’d like to see it for yourself before we dive into the actual implementation details.
Implementation
Installation
via NPM
npm install --save focus-visiblerequire('focus-visible')via UNPKG
<script src="https://unpkg.com/focus-visible@latest/dist/focus-visible.min.js"></script>Styling
The polyfill adds a .focus-visible class on elements that match the :focus-visible state, so styling is pretty straightforward.
/* Remove outline for non-keyboard :focus */
*:focus:not(.focus-visible) {
outline: none;
}
/* Optional: Customize .focus-visible */
.focus-visible {
outline: lightgreen solid 2px;
}⭐️ Bonus tip: a better alternative to outline
*:focus {
outline: none;
}
/* box-shadow outlines rounded corners! */
.focus-visible {
box-shadow: 0 0 0 2px lightgreen;
}Noticed how I used :focus-visible on this site? Hit me up on Twitter if you find this useful! 😁