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
:focus
outline 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-visible
require('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! 😁