Communicates progress through the ordered steps of a wizard or multi-step flow.
Stepper renders as an ordered list (<ol>) by default, and each Step renders as a list item (<li>). The ordered list communicates that the sequence is meaningful, so assistive technology reads the steps in order. The container element can be changed with the as prop when a different element is more appropriate.
Each step's marker is a decorative Coin, and the connecting line is drawn with a CSS pseudo-element — neither enters the accessibility tree. A step's meaning comes from its visible label. A minimal stepper renders no label element at all; the label becomes the step's accessible name through aria-label on its control instead, so every step keeps one.
Because the marker is hidden, a status shown only in the marker would not be announced at all — which is the case for complete, whose check is purely visual. The two attention statuses therefore carry text of their own. A critical step renders a visually-hidden "has errors" and a caution step "needs attention", inside the step's control so it joins the step's accessible name: an errored Goal step announces as "Goal has errors".
The one exception is a minimal step that is also a link or button. There the label names the step through aria-label, which replaces the control's contents for assistive technology and would swallow the hidden text — so the announcement is appended to that name instead ("Goal, has errors"). A non-interactive minimal step keeps the hidden element, because aria-label on the plain element it renders is prohibited and not honored.
Each label also holds the width it would need in the bolder style the current step uses, so a step doesn't resize when it becomes the current one. That width is reserved by a CSS pseudo-element, so the label's text is never duplicated: the step's accessible name, its text content, find-in-page, and text selection all see exactly one label, and there is no extra element for assistive technology to reach.
The orientation prop (horizontal or vertical) only changes the visual layout — the markup, roles, reading order, and ARIA are identical in both, so the experience is the same for assistive technology.
A step with an href renders as a link (a), and a step with an onClick but no href renders as a button; the whole marker-and-label area is the target. Steppers without href or onClick are non-interactive progress indicators with no keyboard interaction of their own.
Interactive steps are reachable and operable with the keyboard through the native link/button they render. A visible focus ring is shown on keyboard focus.
A step with isDisabled is rendered inert by PlainButton: its href is dropped, clicks are blocked, and it is marked aria-disabled="true". This keeps not-yet-available steps from being navigated to. A disabled step that isn't interactive (no href/onClick) still exposes aria-disabled="true" on its control, so the disabled state isn't conveyed by color alone.
| Element | Attribute | Description |
|---|---|---|
| Current step | aria-current="step" | Set on the current step (its control element) to expose which step the customer is on. |
| Step marker | aria-hidden="true" | Applied to the marker coin so the decorative number/icon/dot is hidden from assistive technology. |
| Disabled step | aria-disabled="true" | Marks a disabled step's control — set by PlainButton (which also blocks clicks) when the step is interactive, and on the control directly otherwise — so the state is exposed regardless of interactivity. |
The icon or number inside a marker is decorative and hidden from the accessibility tree, so it is never announced. The visible label — or the control's aria-label in a minimal stepper — provides the accessible name.
A step that is both isCurrent and critical or caution is unchanged in the accessibility tree by its status: it keeps aria-current="step", and the status announcement simply joins its accessible name. The status only wins on the marker's appearance.
Stepper uses color to distinguish complete, current, upcoming, disabled, and flagged steps, but color is never the sole signal: completed steps also show a check, the current step is bolded and carries aria-current, disabled steps are aria-disabled, and the two attention statuses each add a distinct icon plus visually-hidden text. Keep labels meaningful so the state is understandable without relying on color perception.
The two attention statuses are also distinguishable from each other without color. critical shows the circled-exclamation error icon and announces "has errors"; caution shows the triangular warning icon and announces "needs attention" — so the shape and the text both separate them, not just red from yellow.
The exception is content="minimal", whose markers are bare dots in every state. The announcements are unchanged there, so assistive technology still gets both statuses, but sighted users are left with the coin's color alone — so don't use minimal for a flow where a step's status carries weight.
Neither marker is the message. They point at the step to return to; the errors themselves stay where they can be read and fixed — as inline field errors on the step, and in any summary on a review step.