Go Back

Interactive elements


Keyboard accessibility is one of the most important aspects of web accessibility. Many users, especially people with motor disabilities and blind people rely on a keyboard to navigate through the web. These people need accessible focus indicators to enjoy the web.

A very common practice is to hide the default focus indicator because it's ugly and different across browsers, and that's okay. The mistake is to not provide a proper focus state. We must provide accessible focus states on our apps.

button:focus {
  /* It's okay to do this... */
  outline: none;

  /* ...if we build a custom indicator */
  box-shadow: 0 0 0.3rem 0.3rem tomato;


In the exercise page, there are a 4 interactive elements: buttons, links, and 2 form controls. However, we can't easily access them through the keyboard.

You can use Tab and Shift + Tab to go forwards and backwards respectively across the interactive elements.

There aren't any explicit design requirements, so you can go free style! Adding some opacity or changing the color are common design approaches. Creating both :hover and :focus states is the requirement.

🎯 Goal: Make all interactive elements accessible by keyboard.

Note Safari: By default Tab doesn't work on links and buttons. You need to activate it in Settings > Advanced > [✓] Press tab to highlight each item on a web page.


#1 Custom interactive elements

Creating a custom accessible element sometimes can be a (very) hard challenge. Go try to select "Your role" using only the keyboard and then come back here.

That was an impossible mission, right? No the fun part: there's a trick to avoid the complex task of re-creating an a11y <select> and it involves CSS!

It consists in creating 2 selects and showing them at the right moment. A custom one, visible by default for "mouse based" users, and a native one hidden until it's focused by keyboard.

/* visually hide selectNative  */
.selectNative {
  opacity: 0;

/* show it on focus  */
.selectNative:focus {
  opacity: 1;

/* hide selectCustom on selectNative:focus  */
.selectNative:focus + .selectCustom {
  display: none;

🎯 Goal: Create a simpler select accessible version

This technique is what it's called graceful degradation technique. Add a fallback version when the ideal does not work. The experience is not as good, but it does deliver the essential functionality for everyone. Things won't break for anyone.

#2 Focus only when using keyboard

One of the main reasons to hide the focus indicator on buttons and links is because it's triggered when we click them. What if we could activate :focus only when using the keyboard and ignore it when using the mouse? That's where :focus-visible comes in!

Most browsers do not support it yet, but there is a tiny polyfill: focus-visible.js

<!-- Include the polyfill at the bottom of the page -->
<script src="../assets/polyfill-focus-visible.js"></script>
/* hide the focus indicator always */
.js-focus-visible button:focus {
  outline: none;

/* show the focus only with keyboard */
.js-focus-visible button:focus.focus-visible {
  outline: none;
  box-shadow: 0 0 0.3rem 0.3rem tomato;

🎯 Goal: Add focus-visible and update the buttons and anchor :focus styles to use it.