Communicates progress through the ordered steps of a wizard or multi-step flow.

Stepper

Usage

  • Use Stepper as the progress indicator for a wizard or a flow broken into a fixed, ordered set of steps, where showing which step someone is on and how many remain helps them finish.
  • Do not use Stepper for switching between unrelated views (use Tabs), for a checklist of independent tasks completed in any order (use a checklist), or for a single-step form.
  • Compose one Step per stage inside a Stepper. The Stepper numbers steps automatically (via a CSS counter) — do not pass step numbers yourself.
  • orientation (on the Stepper) is horizontal (default) or vertical. Use horizontal for a progress indicator across the top of a wizard; use vertical for a sidebar/rail guide or a long form chunked into stages down the page. Orientation changes only the visual axis — states, colors, fill behavior, markup, and ARIA are identical.

States

  • Give each Step a statusincomplete (default), complete, critical, or caution. complete shows a check in the marker. status is independent of isCurrent and isDisabled.
  • Mark the stage whose content is on screen with isCurrent. It bolds the label, draws an accent-colored ring around the marker (keeping its neutral surface) with a bold number, and sets aria-current="step". Use it on exactly one step.
  • A step that is both status="complete" and isCurrent keeps the completed marker — solid green with a check — and takes the current step's ring on top of it, so the ring is what marks the current step in either case.
  • Use status="critical" for a step whose errors block completing or submitting the flow, and status="caution" for a step that needs review or attention but does not block. Choose between them on that test alone: if the customer can still finish the flow, it is caution.
  • Do not use these markers as the error message. They point at the step to go back to; keep the inline field errors on the step itself and any summary on a review step, and never replace them with a flagged marker.
  • Only flag a step once something on it has actually been evaluated — never speculatively, and never on every unvisited step.
  • Give a flagged step an href or onClick wherever the flow allows it, so the customer can reach the step they are being pointed at.
  • A step can be both isCurrent and critical/caution — the normal case of returning to fix something. isCurrent behaves identically (including aria-current="step"); on the marker the status wins, keeping its red or yellow coin with the same accent ring drawn around it.
  • critical and caution never color a connector. A flagged step is not a completed one, so the lines around it stay grey.
  • Do not convey either attention status by color alone or separate the two by hue alone. Each marker shows a distinct icon (error, a circled exclamation, for critical; warning, a triangle, for caution) and carries visually-hidden text (has errors, needs attention) that joins the step's accessible name — on a minimal step that is a link or button it is appended to the control's aria-label instead, since that name would otherwise replace the text. Preserve both if you override the marker colors.
  • The connecting line between two steps is green only when both of those steps have status="complete"; every other line is neutral grey. The fill is driven by status alone, so marking a step isCurrent never fills the line into it — keep status accurate, and expect grey lines when someone jumps ahead to a step whose predecessors are unfinished.
  • Use isDisabled for steps gated on earlier ones. Disabled steps are muted and inert (even when given an href) and marked aria-disabled.

Appearance

  • content (on the Stepper) controls every marker: number (default) prints the step's position, icon shows each step's icon (completed steps still show a check), and minimal shows bare dots with no numbers or labels. minimal markers drop every status glyph too — the check and both attention icons — so states differ only by the coin's color; do not use minimal when a step's status carries weight.
  • Marker colors are semantic and fixed by state — solid green for complete, solid red for critical, solid yellow for caution, a neutral surface for current, and plain neutral grey for upcoming and disabled. The current step adds an accent ring around whichever of those its marker already is. Do not recolor them to arbitrary palette colors.
  • The label's treatment does not change with status. A flagged step keeps the ordinary label styling — the status lives on the marker and in the hidden text.
  • size (on the Stepper) sets the marker (coin) size — Coin's scale: sm, md (default), lg, or xl. The connector thickness is fixed. Do not invent custom marker sizes or line widths.
  • layout (on the Stepper) controls width distribution and only applies to the horizontal orientation: fill (default) spans the full container; fit hugs the content with a consistent step width and centers itself, for a compact stepper inside a wider container.
  • In content="icon", set each step's icon to a Pluma icon name. In content="minimal", still set label — it is used as the step's accessible name even though it is not shown.

Behaviors

  • Stepper renders as an ordered list (<ol>) and each Step as a list item (<li>).
  • Give a Step an href to render it as a link (through PlainLink, using the app's router) when it maps to a route, or an onClick to render it as a button. The whole marker-and-label area becomes the target.
  • Disabled steps drop their href, block clicks, leave the tab order, and are marked aria-disabled — so future, not-yet-enabled steps cannot be navigated to.
  • Markers and the connecting line are decorative and hidden from assistive technology; a step's meaning comes from its label.

Content

  • Write labels as short, one- to two-word nouns for the stage, such as Recipients, Goal, Schedule, or Review.
  • Avoid verbs and full sentences in labels; keep them parallel in form across steps.

Implementation Notes

  • React invocation: <Stepper><StepperStep label="Recipients" status="complete" href="/recipients" /><StepperStep label="Goal" isCurrent href="/goal" /></Stepper>.
  • Ember invocation: <PlumaStepper><PlumaStepperStep @label="Recipients" @status="complete" @href="/recipients" /><PlumaStepperStep @label="Goal" @isCurrent={{true}} @href="/goal" /></PlumaStepper>.
  • content, size, and orientation are set on the Stepper and apply to every step (shared via context); label, status, isCurrent, icon, href, and onClick are set per Step.
  • In Ember, import both PlumaStepper and PlumaStepperStep from @customerio/pluma-components/ember.

On this page