CSS Focus State
One big and easy win for website accessibility is focus state. Unfortunately this is too often ignored, and worse, actively regressed. Early in my career I was guilty of:
:focus { outline: none; }
This removes the default browser style and makes keyboard navigation practically impossible. Later I would learn the error of my ways and allow the browser to do its thing. Nowadays, if a website design doesn’t consider focus state — which is common — I’ll take it upon myself to add it and preach the virtues.
Recent Work (in progress)
Many moons ago I build the front-end for Parts Giant. This year we’re rebuilding with a new responsive design to modernise all aspects. Accessibility is a big focus, quite literally. As an example, take these Button states. From left-to-right: default, hover, and focus:
I’ve adopted this focus style across the entire component library.
Logo states from top-to-bottom: default and focus:
The logo links to the homepage as one would expect. There is no hover effect, other than the native mouse pointer, but the focus ring is prominent.
Using the correct HTML element is just as important. The Button component is strictly reserved for <a>
or <button>
elements. The <details>
and <summary>
elements are fantastic too. The browser provides free accessibility and interactivity without any JavaScript. Our Accordion component uses these elements and adopts the focus style:
Robin Rendle on CSS-Tricks has an insightful article on the versatility of these elements.
The Card
The concept of a Card is a common design pattern you’ve seen on many websites. Below is a variant of a product listing card. States from left-to-right: default, hover, and focus:
This component uses the :focus-within
pseudo-class to highlight the entire card when the product link is focused. Below is a reduced code example:
<article class="Card">
<a href="/">Product name</a>
</article>
.Card:focus-within {
outline: 2px solid red;
}
.Card a:focus {
outline: none;
}
.Card a:focus,
.Card a:hover {
color: red;
}
The final trick is to make the entire card interactive. An invisible ::after
pseudo-element on the link can be used to achieve that:
.Card {
position: relative;
}
.Card a::after {
content: "";
display: block;
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
}
Be sure to check out Inclusive Components by Heydon Pickering for a thorough take on the card pattern and many other accessible components.
Extending the Card
Another website I’ve been building has a more complex card component.
The hover state for the main article link is a subtle underline (middle example). Like before, the entire card is clickable with the pseudo-element trick.
To apply the focus state (right most example) I’m using the newer :focus-visible
pseudo-class to apply an outline to the ::after
pseudo-element. This appears with keyboard focus but doesn’t flash visible when clicked.
What make this component fancier is that it has multiple links inside. Alongside the article heading there are category links and a superfluous “read more” link.
All links have a subtle hover underline (far left and right in the screenshot above). I’ve added z-index: 1;
so that they sit above the invisible ::after
pseudo-element.
The category links are also focusable (middle example above). The “read more” link however is not focusable. It’s redundant and only serves as visual sugar. I’ve added the tabindex="-1"
and aria-hidden="true"
attributes.
Stay Focused
When in doubt stick to a bold outline
style. It’s common for the :hover
state to be repurposed for :focus
. This is better than nothing but can be too understated. Don’t forget the useful outline-offset
property to improve visual spacing. I do this on my own website. My button states from left-to-right: default, hover, and focus:
I’ve recently noticed Firefox has started applying the border-radius
to the outline
style. I like the effect on my design but I’m not sure I want that to be default behaviour.