Always worth repeating: all interactive elements should have a focus style. That way, a keyboard user can tell when they have moved focus to that element.
But if you use :focus
alone for this, it has a side effect that a lot of people don’t like. It means that when you click (with a mouse) on an interactive element, you’ll see the focus style. Arguably, you don’t need that feedback as a mouse user, because you just moved your cursor there and clicked. Whatever you think of that, it’s annoyed so many people over the years that they remove focus styles entirely, which is a gnarly net loss for accessibility on the web.
What if we could apply focus styles only when the keyboard is used to focus something, not the mouse? Lea Verou put a finger on this a few years back:
That was in response to Chrome dropping the feature behind a flag. Clever clever.
Fast forward a couple of years, Chrome is releasing it without a flag. They are on board with Lea’s idea:
By combining
:focus-visible
with:focus
you can take things a step further and provide different focus styles depending on the user’s input device. This can be helpful if you want the focus indicator to depend on the precision of the input device:
/* Focusing the button with a keyboard will show a dashed black line. */
button:focus-visible {
outline: 4px dashed black;
}
/* Focusing the button with a mouse, touch, or stylus will show a subtle drop shadow. */
button:focus:not(:focus-visible) {
outline: none;
box-shadow: 1px 1px 5px rgba(1, 1, 0, .7);
}
I might suggest trying those selectors without the button
, making them globally applied!
There is more to dig into, so I’ll link up some more stuff here:
- The Chromium Blog post covers the heuristics of the selector. It’s tricky. It’s like there is an algorithm to determine if
:focus-visible
is going to match or not, which you just largely need to trust. It also covers the idea that Firefox has long had:-moz-focusring
, but the behavior is different enough that they don’t recommend using it if you’re shooting for consistent behavior. - Matthias Ott blogged about it with some good info, like using the official polyfill and how to look at the styles properly in DevTools (there is a new checkbox for it).
- We’ve covered this before. In that, we noted Lea’s tweet that she thought usage would explode when it ships for real. Let’s see (and hope)!
- Our almanac entry has a bunch of details.
Just to note: don’t think that mouse clicks don’t deserve an obvious focus style as well. I often open links in new tabs, and after closing the tab, the focus indicator is very helpful to see where I left of in a list of links for example.
This will come handy in my next project. Thanks for sharing
I agree 100%. A website is much more impressive when they have designed a focus state that works seamlessly with the rest of the design versus when a website just hides the focus state.
A legitimate CSS trick
I’m used to style focus and hover with a comma
.el:hover, .el:focus {}
.Just watch out. Switching to
.el:hover, .el:focus-visible {}
may backfire in browsers without focus-visible support since they discard the whole selector.This “:focus-visible” pseudo selector is awesome…The lack of Safari support is a bummer though…Unless this has changed?
What I do for this situation where you want the focus ring for keyboard users and not for mouse and touch users is just use a dash of JS…
“`
yourButtonElement.addEventListener(‘mousedown’, function(e){
e.preventDefault();
});
This prevents the focus ring from appearing on mousedown on desktop or touch on mobile…
If you want a custom style when the user “clicks” or “touches” your button you can just add and remove classes with JavaScript…via event handlers
Admittedly a plain CSS solution would be easier…So I just wish Safari would support “:focus-visible”
I think the focus ring hiding on click is pretty appropriate as a progressive enhancement, and as such this css would be a good solution even with minimal support. If
:focus-visible
supported, then great, but if not then having the focus ring on a button isn’t the end of the world.My prior js solution was to place a fixed div that covered the screen, and when it received a mousemove event, the div would go away and a class for
no-focusring
would be added to the body classThen on any keyboard input, the div would show again and the no-focusring class would be removed from the body.
So in essence, the focus ring would be disabled only if the mouse had moved, and if the user had js enabled.
This is a useful feature that has a confusing name. By calling it focus-visible, it implies regular focus style is invisible, which may or may not be the actual case.
Please note that this has completely counter-intuitive behavior on textboxes. If you click a button, it will not show :focus-visible styles. However, if you click a textbox, it WILL show :focus-visible styles, and according to the spec, that’s expected, unfortunately.