Skip to content

Steps

Where the user is in a task that takes several pages, like an order or an application. The class goes on an <ol> with one <li> per step, and aria-current="step" says which step the user is on. The steps before it are drawn as done, so that is all the markup has to say.

If the task has no named steps, only a share that is done, Progress is the better fit.

Two things have to be in the markup:

WhatWhy
<ol>, not <ul>The order is part of the meaning, and the screen reader says how many steps there are
aria-current="step" on the <li> the user is onIt is what the screen reader announces as the current step, and what the done steps are read from. On a link inside the step it does neither, since the stylesheet reads it on the <li>

A name on the list, aria-label="Order", tells the steps apart from the other lists on the page.

The stylesheet draws every step before the one with aria-current="step" as done, with a check instead of the number. The screen reader reads the check as “Completed”. There is no attribute for a done step, so the markup cannot say one thing while the drawing says another.

It also means that a user who goes back to an earlier step sees the steps after it as not done again. That is what the indicator is for: it shows where the user is, and what is still in front of them from there. If the steps after it are still saved, say so on the page.

A done step can be a link back to it. The current step and the ones after it are plain text.

The steps share the row. When the list is narrower than 28rem, they stand in a column instead, with the marker at the start of each line: a row of five steps on a phone gives each name less room than one word.

The width that counts is the list’s own, not the window’s, so the indicator also stands in a column in a narrow side panel. The list is a size container for that reason, and takes the width of the box it stands in. In a flex row, give it a width of its own, flex: 1 for example: a size container has no width from its content, and without one it shrinks to nothing.

ClassWhat it is
.lt-stepsThe list of steps
VariableDefault
--lt-steps-marker-sizevar(--lt-spacing-7), the circle with the number or the check
--lt-steps-done-iconvar(--lt-icon-check), the check on a done step, the same as in the Checkbox. Any image will do: a url() or a gradient
--lt-steps-done-text"Completed ", what the screen reader reads for the check, with the space before the step’s name

The marker is ::before on the step’s link, or on the step itself when it has no link, and the line to the next step is ::after on the step. Translation shows how to set --lt-steps-done-text by the language of the page.

See Customising for how the variables and the layer work.

What a screen reader is given when the recipe above is used, step by step. It is checked through AT-SPI, the interface Orca reads, in Chromium and Firefox, on every change, by check-accessibility-tree.ts, and every change the check lists as a mutation must make it fail.

  1. Focus li:nth-child(1) > a: focus on the link "Completed Site".
  2. Focus li:nth-child(2) > a: focus on the link "Completed Survey".
  • The number on the marker is decoration, with an empty alternative text: the list already says which step it is, and “3 Walls” would only repeat it.
  • The check is read as “Completed” in front of the step’s name. When the done step is a link, the text is part of the link’s name, so a keyboard user who tabs to it hears it too.
  • Say where the user is in the page’s heading as well, such as “Walls, step 3 of 5”. The indicator is a list someone has to find, and the heading is what the screen reader reads first.
  • Do not use the indicator as the only way between the steps. Every step still needs its own buttons forward and back.