One thing I love about the new’ish :focus-visible
pseudo-state is that it allows me to create bigger, bolder, and more obvious focus states for my keyboard users than I normally would with a :focus
pseudo-state that might flash or linger on a click. For example, my default :focus-visible
is pretty chonky…
*:focus-visible {
outline: 3px solid black;
outline-offset: 0.25rem;
border-radius: 0.125rem;
}
A three pixel slightly rounded outline offset from the element, it’s hard to miss. But you will never see it unless you’re using your keyboard.
Focus states are one of the areas of accessibility that are easy to neglect when you don’t use your keyboard to navigate everyday. I went on a deep dive fixing some accessibility issues on my site last month and found all kinds of issues with focus. So I fixed them using :focus-visible
.
ArticleList
The first was my ArticleList component. I use this pattern often but somehow managed to nuke the focus state from existence. How did I do such a bad job? Using a reduced test case, I found the culprit…
❌ <a href><h2>Title goes here</h2></a>
✅ <h2><a href>Title goes here</a></h2>
After years of moving furniture, my HTML5 block links weren’t so blocky anymore. I (re)learned that you need a { display: block }
on anchors that wrap block-level elements to get focus states. I prefer focus states that hug the text rather than a full width block outline, so I refactored the HTML.
That’s a good representative of my default :focus-visible
in action. Now onto more, funner, focus states.
ProjectCard
On my new Projects page I have little project cards with images that View Transition to the detail view. The project logos aren’t uniform, so to reduce the cacophony of colors I use CSS filter
to desaturate the image, lower the opacity, and normalize with a sepia filter.
.project img {
view-transition-name: var(--name);
transition: filter 0.2s ease-in-out;
filter: grayscale(100%) sepia(0.2) opacity(0.75);
}
.project :hover img,
.project :focus-visible img {
filter: none;
}
On mouse :hover
I set the image goes to full color. I made it do the same for :focus-visible
and added the beefy outline as well.
This is great example of where I want :focus-visible
instead of :focus
. I wouldn’t want that :focus
state and its outline lingering as part of my view transition on every click.
StoryCard
My new stories page has some funky focus states as well. Because I’m using a “sci-fi rectangle” shape for my StoryCard. On :hover
I do nothing but cursor: pointer
, so the :focus-visible
state ends up as a bit of an Easter egg for keyboard users. On :focus-visible
the image link shrinks down and the outline (with one rounded not-so-sci-fi corner) appears.
This is another example where I wouldn’t want those images to shrink down on :focus
, so I’m able to have even bigger and more obvious focus states with :focus-visible
.
My Bookshelf
The last place where I added :focus-visible
states was to my favorite child, my bookshelf. I had managed to break focus states again, so I was happy to re-add them back.
The image link focus and <details>
dropdown focus are pretty obvious now where they weren’t visible at all before. I made a choice to differentiate the :checked
of my filters based on keyboard focus as well. Clicking gets you a subtle black outline, while keyboard input gives you a beautiful blue outline with a hint of background color. It’s overkill, but it’s my overkill, so I love it.
You could say I’m a fan
I’ve loved :focus-visible
ever since I first heard Alice Boxhall and Rob Dodson talk about it. I can’t tell you how often I’ve heard “Can we remove the blue outline in Safari?” and had to add an outline: 0
CSS crime. That complaint was so prevalent that you have to TURN ON tab focus in Safari now. I think that’s a mistake but, c’est le web.
Good news! We don’t have to commit CSS crimes anymore! :focus-visible
solves the “I want keyboard focus but not click focus” problem by treating keyboards and pointers separate. For me it’s an opportunity to enhance experiences for keyboard-only users.
I know there’s folks who think we shouldn’t use this new-fangled pseudo-state, that we should use :focus
instead like the old days. There’s a good argument behind that but in a world where a lot of (most?) sites disable focus states entirely, this seems like a good path forward. And sometimes, it might be a better keyboard experience for users than a little color change on hover (§1.4.1).