Creating accessible progress indicator in React
By: Dan Homola
Tags: Accessibility, React

When creating our registration application our UX designer came up with the following design (screenshot taken from our storybook):

It was relatively easy to implement using React, so we wrote it and forgot about it.

Several weeks later, we submitted the alpha version of the app to our accessibility testing consultant (as we do with all our apps). The results were that among other minor accessibility problems, the most serious was that the progress indicator (we call it thermometer) is completely inaccessible to screen readers. Not invisible, even worse — annoying, because when read using a screen reader it produced seemingly irrelevant words.

The accessibility problems can be split into these points:

There was no indication this component represents a progress display

The circles were not numbered and labelled in any way

The lines connecting the circles did not provide information about how much of the way they were filled

There was no explanation what A***** D*** means (this was explained in the rest of the app, so a seeing users could easily read it, visually impaired users however could not)

We will discuss how we fixed all these problems in the rest of this article.

Declaring the semantic meaning

First, let’s focus on how to declare that our component is a multi step progress indicator. We followed our consultant’s recommendation: The whole component is an ordered list where the list items consist of the circle and optionally the connecting line. Bellow there is the source code of the main component:

Thermometer component

As we can see, it renders an ordered list with a descriptive aria-label attribute that tells the user what it represents.

The list takes two props: items that specify name, icon and other properties of the individual steps, and position – two numbers that indicate which step is currently active and what is the percentual progress to the next one.

Rendering the circles

Next, let’s see how the Segments (i.e. list items) are rendered. Bellow is the code for the Segment component:

Segment component

The Segment component renders one circle and if it is no the last one, it renders the connecting line as well. The important thing here is that the li element is marked as aria-current when appropriate. This ensures that screen reader reads the current step like

Account, current step

ensuring the user knows what the current step is.

Other than that, the component is relativelly straight-forward, it computes some derived props for the Point and Line components (see bellow) based on the current position on the thermoeter.

The Point component represents the circle on the thermometer. It has a name, an icon and status, that indicates whether it is the current step, step already completed or one that is still to be visited. The whole code is listed bellow:

Point component

Point renders a div with some styling that ensures it is a circle of the right color and size depending on the status. Active step is larger than inactive for example.

Inside of the div , there are two span s – one for screen readers only and one for displays (hidden from screen readers using the aria-hidden attribute). The reason for this duplicity is that we needed to provide additional description for the screen reader users. In our case it was a note that the name of the person being registered is redacted and therefore is spelled A***** D*** instead of Arthur Dent.

You may wonder why we did not simplify this to something like

Simplification attempt

While this would work relatively well, the screen reader would read the two parts separately. In other words the user would have to press a button to hear props.description as well. The way we wrote it, both strings get read at once.

Rendering the connecting lines

The last part we haven’t shown are the lines connecting the circles. This is the responsibility of the Line component:

Line component

The visual part of its markup is simple: just a div contaning two overlapping div s that emulate the partially filled progress bar. It's the ARIA part that's interesting!

As we can see, the main div has several ARIA attributes, so let's explain those one by one.

The role attribute declares what the component represents semantically – in this case a progressbar. There are quite a few roles available in the WAI-ARIA standard.

We used aria-label here to give name to the progress bar. In this case the title of the related step is used. This ensures the user knows which step the progress bar corresponds to.

Finally, the aria-valuenow specifies the curernt progress value while aria-valuemin and aria-valuemax specify the lowest and highest possible values. These are used by the screen readers to compute the percentual progress of the progress bar. This means that somthing like

aria-value* attributes example

would be read as

50 percent

Conclusion

On a simple real life example we’ve shown how to make React components accessible to visually impaired users and hopefully inspired you to revise your own components with this perspective in mind. After all, accessible components are better components.