Buttons and keyboard accessibility

person sitting at a keyboard

As someone who’s experienced chronic hand and arm pain, I prefer to use the keyboard as much as possible, to reduce the strain of constantly moving between the keyboard and mouse. There are also some folks who rely completely on the keyboard (or another device apart from the mouse) as their primary navigation tool on the web. Let’s talk a little about how we can ensure our buttons are accessible to these users.

The magic of the <button> element

Let’s say you’ve got a widget with a series of items, where you can click on each one to lead to more details:

#1
#2
#3
#4
#5
#6

Not bad, it works (if you’re using a mouse). But even if you’re using a mouse, you can probably already sense something is off. First of all, there’s no visual indication that these are actually buttons that you can click on. And when you do click them, there’s no visual feedback that you’re in the process of clicking. Finally, try navigating these items and selecting them using just your keyboard (‘tab’, ‘shift-tab’, ‘space’, and ‘enter’ keys). It won’t work.

If you look at the HTML code in DevTools, I’ve written these items as HTML <div> elements:

<div>#1</div>
<div>#2</div>
...

<div>s are generic HTML elements that don’t mean much of anything to the browser. They’re useful to group items together and add styling, but that’s about it. Even though we’ve added click listeners to each item, the browser doesn’t ‘know’ that these are actually buttons that are meant to be interacted with! And so, we’ve left keyboard users with a pretty crappy experience - they’ll need to use some sort of added software/simulator just to be able to use this widget.

So, let’s change all of these to <button> elements instead and see what happens:

<button>#1</button>
<button>#2</button>
...

Interesting! There are some immediate visual changes: a border of sorts has been added, and the text is now centered within each item. Also, with the mouse, notice that while clicking on the element, there is a subtle change in the appearance (hold down the mouse to see this more clearly). Finally, let’s see if we can now navigate these buttons using the keyboard: you should be able to use ‘tab’ and ‘shift-tab’ to move forwards and backwards, and then ‘space’ or ‘enter’ to select them. Notice that as we navigate this way, the browser is adding an outline to clearly show which button we’re on.

We got all of this functionality simply by changing the divs to buttons! No extra code, no CSS, no JavaScript, nada!

Accessibility by default

One of the most important things to understand about keyboard accessibility (and accessibility on the web in general) is that the browser tries to be accessible by default. Now, this doesn’t mean the browser always gets it right, but it does mean that oftentimes, we just need to get out of the way and let the browser do its thing. In this case, simply using the correct <button> element enables a slew of important accessibility features.

Using the correct, semantic HTML element isn’t just good for keyboard accessibility - it’s also necessary for folks with visual impairments, who might be using a tool like Apple’s VoiceOver to read the page. By using the <button> element, VoiceOver will announce to the user that these are in fact buttons that are meant to be interacted with.

Styling

Alright, so now we’ve made sure that we’re using the correct HTML element here. But we still want to make it pretty, right? Let’s try adding a little CSS, while making sure not to diminish the browser’s accessibility features.

(Side note: I am not a designer, so please take my specific color choices with a grain of salt!)

First, let’s clear out the background and add a nice border. I’ll also change the cursor to a pointer, to indicate functionality to our mouse users.

.fancy-button {
background: white;
border: solid 1px hsl(196, 40%, 50%);
cursor: pointer;
}

(I’m using the hsl color syntax here for clarity.)

Now, remember how the items (once we properly labeled them as buttons) changed subtly while clicking them? When a button is being clicked on, either through mouse or keyboard, it triggers the :active state, which we can target in our CSS. We can also add a light hover effect using the :hover state. In keeping with the browser’s original behavior, these should ideally be subtle color changes:

.fancy-button:hover {
background-color: hsl(196, 50%, 85%);
}
.fancy-button:active {
background-color: hsl(196, 50%, 70%);
}

Finally, for our keyboard users, we can style the outline that automatically appears when they’re navigating with the ‘tab’ key. When a keyboard user focuses on a specific item, it triggers the :focus-visible state (note this is different from the :focus state, which will trigger for both mouse and keyboard users). Using this state, let’s style the outline as a bright, contrasting color, and also add an outline-offset that will separate the outline from the button to make it more prominent:

.fancy-button:focus-visible {
outline: solid 2px hsl(14, 90%, 60%);
outline-offset: 2px;
}

Here is the final product. Notice all the different states of the buttons, and be sure to test it out with your keyboard as well:

We’ve created something much more accessible and clear - not just for keyboard users, but for all our users!

Conclusion

Keyboard accessibility doesn’t have to be a scary thing, and I hope this article gives you some ideas on how to approach it. In general, when we’re building something that may be complicated/challenging for keyboard users, we’ll want to:

  1. Figure out what the browser/device does by default, and know why it does that.
  2. Figure out what tools are given to us through HTML and CSS, before reaching for JavaScript.
  3. Incrementally add styling and scripting/interactivity, while double-checking that the accessibility features are still there each step of the way.

Of course, this is a huge topic with lots to explore, and I encourage you to dig into it further! Your keyboard users will thank you 😉.

Further Resources