Web Accessibility: Tabbing & Focus

avatar
Marshal Murphy
April 27, 2020 - 8 MIN READ

Accessibility.

Like Internet Explorer, accessibility is something that is often overlooked and forgotten in web development.

But unlike the former, it actually matters 🤣.

Accessibility means writing our code such that people of all capabilities are able to interact with our content equally. While the power is firmly in the grasp of the mighty mouse or touch screen, it’s important that the buck doesn’t stop there.

I’ll admit, when I first began my journey into web development, I would set focus outlines to zero, use floats that broke the tab order, and I never really thought about properly adding labels or using arias for assistive technologies.

A few years in, I plainly see the error of my ways.

Feature

INCLUSIVITY!

Everyone deserves an equal opportunity to use our digital products, and neglecting those who may be unable to use the standard tools of interfacing with a UI isn’t very inclusive.

Excluding the be-a-good-person factor, try taking a few minutes to look through some job postings for front-end development and you will find that most quality listings expect you to be experienced with building accessible UIs.

That brings us to this week’s lesson: Learning how to build an accessible user interface, the easy way.

There is a ton of dense jargon out there around accessibility that can be quite daunting, so here I have boiled it down to the essentials. I will also show you how to audit your UI to see just how accessible it is.

In practice, web development for accessibility can be roughly divided into the categories of Focus, Semantics, and Styling.

Today we are going to focus on Focus, with the follow-up to this article covering Semantic HTML & ARIAs.

As for styling… best leave that for the designers.

We are here to build.


Focus (Keyboards & Tabbing)

Can the user access every interactive element on the page by using only the tab key?

Interactive elements, such as input fields and links, are ones that become highlighted with a blue focus ring when selected. These are elements that the user can interact with, and the user knows this because of the focus ring.

You can customize the focus ring, or implement your own :focus styling, but never should an interactive element be without the visual indication that it can be interacted with.

As you tab through a web page, the order that you encounter these interactive elements should be logical and predictable. For English readers, this means tabbing should progress from left to right, top to bottom.

Try filling out this mouse-disabled form using only your keyboard to get a sense for how this should function.

The attribute that these elements possess by default, the one that makes them tabbable, is tabIndex = 0.

The browser sets this attribute for interactive elements automatically, and the order in which the user encounters these elements when tabbing is directly related to the DOM order; that is, the order in which they are written in the HTML.

If you set tabIndex = -1, the element will be removed from the tab order.

If you set tabIndex to any positive number, that element will become the first in the tab order. This is an anti-pattern, and will cause tabbing to jump around the page unpredictably. So... just don't do it, okay?

Let’s run through an example: dropdown menus.


The Custom Dropdown Menu

For a custom component, it will not be immediately discoverable by the browser as an interactive element. Let’s think for a moment what functionality is required from a keyboard perspective:

  • Dropdown menu can be tabbed to.
  • Menu can be opened with the Enter key ( keycode === 13 ).
  • Can tab through the open dropdown menu.
  • Menu closes automatically when tabbing through the final menu item.
  • User continues along the same tab order after menu is closed.

It is far easier to ignore this functionality than build it. But what sort of guide would I be if I didn’t show you how this is done?

AND since I love working with React, let’s pull down a repo I’ve prepare for you and implement this functionality together.

You may find the starter repo Here, and the final completed repo Here.

Start by opening your terminal and navigating to the directory where you keep your code projects (documents, desktop, w/e), pull down the repo, install the dependencies and launch the React app.

git pull https://github.com/marshallmurphy/articles/accessibility-part-1.git
cd accessibility-pt1
npm install
npm start

You can now view the UI at localhost:3000.

I have set up a simple navbar with some links and a styled dropdown button. Very plain, but it’ll do what we need it to.

If you try hitting the tab key, you will see that you are able to tab through each element in the navbar, as well as the link in the body.

First thing’s first, let’s add a dropdown to this button, with some styles. In Dropdown.js, update it with the following block of code below the button tag:

…and let’s apply some styles to this menu in dropdown.module.css:

Great! We now have a menu that is stuck being open and looks like this:

Feature

While we know these will become links, there is no visual indication that this is what they are. For the sake of argument we are using divs, but ideally we would use anchor tags.

Let’s apply some focus and hover states to the button and menu items.

Feature

Super duper.

Tabbing through the navbar again, you will notice that we are unable to tab down the dropdown menu. Let’s fix that! Update each item in the dropdown menu with tabIndex='0':

Feature

The dropdown menu is now accessible by keyboard!

The next step now is to add the ability to open and close this menu. To do this, we will use React Hooks to create an active state and a function to toggle this state true or false. We will then add this function to the button, and hide the menu behind active === true.

Update your Dropdown.js as follows:

Feature

Okay, we’re making progress. The menu is now able to opened and closed by both the 🐭 and the keyboard.

The final piece of functionality is a nice-to-have, but is fairly easy to implement and makes the user experience complete.

We want the menu to close if the user tabs through the final item.

To accomplish this, we will write a closeMenu() function that will close the menu if the user hits the tab key (keycode === 9), and we will add this function as an onKeyDown trigger to the final menu item only.

… then define the function underneath your [active, setActive] hooks definition:

Looking at the closeMenu(e) function, you can see that we are closing the menu if the user hits the tab key while also is not holding the shift key. Shift + Tab allows you to reverse direction when tabbing, and so we want to avoid closing the menu in this case.

Feature

There we have it: a keyboard accessible dropdown menu that could most certainly use some love from a designer.

Now, this menu would still fail accessibility for screen readers and semantic HTML, but we will continue that discussion in the follow-up article to this one.

For now, how about we round this off by quickly discussing and interesting case, Focus Traps.


Focus Traps & Modals

We have covered tabbing, DOM order, adding / removing elements from the tab order with tabIndex, and we’ve walked through implementing our own keyboard-accessible custom dropdown menu.

The last thing that is important to touch upon are focus traps and modals.

A focus trap is when a user tabs to a specific interactive element and is “trapped” there until they interact with the element. They can not navigate away until the interact with it.

For nearly every single use case, implementing a focus trap is a very poor user experience and should be avoided at all costs.

The only exception to this rule that I have encountered in my career are modals. A modal, when triggered, displays an overlay that darkens the screen content and mounts a container that can display, well, anything really, inside a box.

Feature

A well-implemented modal will prevent the user from scrolling and tabbing the background content when it is visible. That is a focus trap.

Using a library like bootstrap will provide this functionality for you out of the box. However, if you would like to create your own modal, you will need to write some code to implement the focus trap.

The following are a list of considerations you would need to think about when creating a focus trap for a custom modal component:

  • When modal is opened, set all DOM elements with a tabIndex = 0 to tabIndex = -1, and those inside the modal to tabIndex = 0. This creates a new tab order for the modal only.
  • Trigger focus() on the first interactive modal element to focus to show the user where the tab order starts.
  • Tabbing through the last interactive element focuses the first, and shift-tabbing up through the first element focuses the last.
  • When the modal is closed, set all DOM elements with tabIndex = -1 back to tabIndex = 0, and those inside the modal to tabIndex = -1, establishing the original tab order.
  • Trigger focus() on the last element the user interacted with to maintain the tab order progression.
  • Use CSS to fix the modal in place and set overflow: hidden on the page to prevent scrolling.

Now that is a LOT of work! I highly suggest using a modal library like Bootstrap for this sort of thing.

Just be sure you test any modal library for accessibility before working through your implementation.


We’ve covered quite a bit today!

While it’s incredibly easy to ignore keyboard-only functionality, and you might even be able to get away with it for a while, you will never be able to say that you’ve created a truly great user experience.

Ensuring that all users can interact with the interfaces you build is essential to make the web a better, friendlier place. AND… You’re halfway there!

All you really needed to do for this half of the puzzle was to make sure people can navigate the page using only the tab key.

Following this article is the second half to building rich, fully accessible UIs: Web Accessibility: Semantic HTML & ARIAs.

Until then, know that you are doing a world of good, and have become ever-so-slightly more employable.

Marshal Murphy 2020