Focus Style CSS for Keyboard Navigation

White keyboard

Making fully accessible websites should be a priority for any front end developer. Most HTML has accessibility baked in so our lives should be slightly easier. That said, as we create custom components and customise styling, ensuring all users are given the best experience can be a bit trickier.

Table of Contents

Keyboard only focus state

An issue that I came across recently was that of needing keyboard only focus state on a button. As the button triggered an action on the page and did not navigate the user away from that page the focus state stayed highlighted. This is not particularly beneficial for any user, as the button has already been interacted with. In short, :focus is applied not only when you have ‘focused’ on the element, but also just after you have clicked on it.

My button needed to have a focus state added when a user was navigating by keyboard, but not by any other means. If they use the tab button, focus is applied. But not when using a mouse.

What about :focus-visible?

I came across :focus-visible, and it looked like the perfect solution. Mozilla says:

This selector is useful to provide a different focus indicator based on the user’s input modality (mouse vs. keyboard).

The difference between :focus and :focus-visible is that :focus-visible comes into play when a user navigates with a keyboard. A mouse does not trigger it. Sounds great!

Unfortunately it is only implemented within Chrome, and even then it is behind a flag. Very limited uptake.

There is a polyfill available which adds this functionality to all browsers, but I have an issue with adding extra JavaScript when there may be a different solution. An alternative to :focus-visible is needed.

Using HTML and CSS

Turns out there is a very elegant solution to this, as Roman Komarov talks about in his article. Roman shows that it is possible to achieve keyboard only navigation with focus state for buttons and links using an inner span or div.

Use the tabindex

By setting the inner span or divs tabindex to -1, it can be made to be artificially focusable. MDN covers this in their piece on the tabindex saying:

A negative value (usually tabindex=”-1″) means that the element is not reachable via sequential keyboard navigation.

What this means is that the element can be focused on by a mouse, but not a keyboard. So when the outer element (button, or link) gets clicked the inner element gets the focus. The HTML looks like this:

<button class="button" type="button">
    <span class="button_inner" tabindex="-1">
        Click away!
    </span>
</button>

Removing the :focus

Now that conditional focus has been set up using the HTML elements, appropriate styling needs to be added.

Firstly let’s remove :focus off both elements. This is not needed as we are looking for CSS focus only on keyboard.

.button:focus,
.button_inner:focus {
    outline: none;
}

Then focus styling is added to the inner span or div when the outer element has focus, and only then.

.button:focus > .button_inner  {
    border: 2px solid red; /* A terrible style to use, but easy to see for this example! */
} 

Solution to CSS focus only on keyboard

Final code to solve our keyboard navigation with focus state problem: See the CodePen.

To test, click the button and you will see that no focus is added. Then navigate with your browser tab key. This will highlight the button with a red border.

Hopefully :focus-visible will be rolled out to more browsers as that would be a far cleaner solution. However, for now this alternative is fairly easy to implement and achieves the desired result! Now users can get a focus state only when they use the keyboard.

Web accessibility is a topic I feel quite strongly about. If you are interested, you can read more about the ‘Inclusive web’ and why it should matter more to businesses.