Stepper

Vertical stepper with per-step content (Material vertical Stepper equivalent) for setup flows and long forms broken into stages. Markers reuse the tracker's state language — done: success circle with a check; current: accent circle; upcoming: sunken circle — joined by a 2px connector down the marker column that turns success-colored for segments owned by done steps. Two flavors share one skin: class-controlled (<ol> of <li class="stepper-step">, content shown by adding .expanded — app/server drives state) and zero-JS expandable (<div class="stepper"> of <details class="stepper-step"> whose <summary class="stepper-header"> holds the marker + title — native disclosure does the toggling). For a compact horizontal progress readout use .tracker instead.

Root: .stepper on <ol>, <div>

Examples

Example 1
  1. 1Project details

    Name and key chosen.

  2. 2PermissionsOptional

    Choose who can view this project.

  3. 3Invite team
<ol class="stepper"><li class="stepper-step done"><div class="stepper-header"><span class="stepper-marker">1</span><span class="stepper-title">Project details</span></div><div class="stepper-content"><p>Name and key chosen.</p></div></li><li class="stepper-step current expanded" aria-current="step"><div class="stepper-header"><span class="stepper-marker">2</span><span class="stepper-title">Permissions<span class="stepper-optional">Optional</span></span></div><div class="stepper-content"><p>Choose who can view this project.</p></div></li><li class="stepper-step"><div class="stepper-header"><span class="stepper-marker">3</span><span class="stepper-title">Invite team</span></div></li></ol>
Example 2
1Create account

Done on sign-up.

2Verify email

Click the link we sent you.

<div class="stepper"><details class="stepper-step done"><summary class="stepper-header"><span class="stepper-marker">1</span><span class="stepper-title">Create account</span></summary><div class="stepper-content"><p>Done on sign-up.</p></div></details><details class="stepper-step current" open aria-current="step"><summary class="stepper-header"><span class="stepper-marker">2</span><span class="stepper-title">Verify email</span></summary><div class="stepper-content"><p>Click the link we sent you.</p></div></details></div>

Contract

Structure

SelectorRequiredDescription
.stepper-steprequiredOne step per <li> (in an <ol>) or <details> (in a <div> wrapper — <details> cannot be a child of <ol>). State classes on the step: .done, .current (at most one), none = upcoming; .expanded shows the content on the class-controlled flavor, [open] does it on the details flavor.
.stepper-step .stepper-headerrequiredMarker + title row. A <div> on the class-controlled flavor; the <summary> on the details flavor (making the whole row the disclosure trigger).
.stepper-header .stepper-markerrequired24px circle with the step number. Done steps visually swap the number for a check via CSS — keep the number in the markup.
.stepper-header .stepper-titlerequiredStep name (semibold). May contain a nested .stepper-optional.
.stepper-optionaloptionalSmall subtle annotation under the title, e.g. “Optional” or a completion summary.
.stepper-step > .stepper-contentoptionalThe step's body, indented under the title with the connector running down its start edge. Hidden unless the step is .expanded or an open <details>.

Accessibility

OnAttributeLevelWhen
.stepper-step.currentaria-currentrequired

Prefer the <ol> flavor so step count and position are announced; put aria-current="step" on the current step. The details flavor trades list semantics for built-in keyboard expand/collapse — the summary is natively focusable and toggles on Enter/Space. Don't mix flavors in one stepper. The done-state check is decorative CSS; add visually-hidden “(completed)” text on done steps if completion matters to screen-reader users. Content hidden by a closed step is genuinely hidden from the accessibility tree — don't put the only path forward (e.g. the submit button) inside a collapsed step.

Nesting

Allowed in:

  • cards
  • modal bodies
  • settings and onboarding panes

Never inside (or containing):

  • .stepper
  • .tracker