Flux 2.0

Component

Stepper and wizard

Numbered steps that show progress through a task, and the wizard recipe that validates each one.

Plain-text source, for agents and clipboard use: /components/source/stepper.txt

When to use it

A Stepper shows where someone is in a task with a fixed order: which steps are done, which is current and which remain. The wizard recipe pairs it with one form section per step, validates each step with the platform’s constraint checks before the next one opens, moves focus to the new step’s heading, and ends on a review that reads back what was entered. Numbering is the meaning here, so the markers are numbered.

Not this
Not for a form short enough to fit on one screen. Not for steps that can be done in any order; use sections or Tabs. Not for showing location in a hierarchy; use Breadcrumbs.

Examples

Progress

Two steps done, the third current. Completed markers carry hidden text, so the tick is not the only signal.

  1. Completed: Lead traveller
  2. Completed: Departure
  3. 3Rooms
  4. 4Review
html
<ol class="flux-stepper" aria-label="New group booking">
  <li class="flux-stepper__step" data-state="complete"><span class="flux-stepper__marker"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3.5 8.5l3 3 6-7" /></svg><span class="flux-visually-hidden">Completed: </span></span><span class="flux-stepper__label">Lead traveller</span></li>
  <li class="flux-stepper__step" data-state="complete"><span class="flux-stepper__marker"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3.5 8.5l3 3 6-7" /></svg><span class="flux-visually-hidden">Completed: </span></span><span class="flux-stepper__label">Departure</span></li>
  <li class="flux-stepper__step" aria-current="step"><span class="flux-stepper__marker">3</span><span class="flux-stepper__label">Rooms</span></li>
  <li class="flux-stepper__step"><span class="flux-stepper__marker">4</span><span class="flux-stepper__label">Review</span></li>
</ol>

With an error

A step that was completed and then invalidated, such as by a supplier declining, returns with an error state and hidden text that says so.

  1. Completed: Lead traveller
  2. Needs attention: Departure
  3. 3Rooms
  4. 4Review
html
<ol class="flux-stepper" aria-label="New group booking">
  <li class="flux-stepper__step" data-state="complete"><span class="flux-stepper__marker"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3.5 8.5l3 3 6-7" /></svg><span class="flux-visually-hidden">Completed: </span></span><span class="flux-stepper__label">Lead traveller</span></li>
  <li class="flux-stepper__step" data-state="error"><span class="flux-stepper__marker"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 4v4.5M8 11.5v.01" /></svg><span class="flux-visually-hidden">Needs attention: </span></span><span class="flux-stepper__label">Departure</span></li>
  <li class="flux-stepper__step" aria-current="step"><span class="flux-stepper__marker">3</span><span class="flux-stepper__label">Rooms</span></li>
  <li class="flux-stepper__step"><span class="flux-stepper__marker">4</span><span class="flux-stepper__label">Review</span></li>
</ol>

Wizard

Press Next with the fields empty to see each step validate. Focus moves to the first field with a problem, and to each new step’s heading. The review reads back the answers, and submitting dispatches flux-wizard-complete.

New group booking

  1. 1Lead traveller
  2. 2Departure
  3. 3Review

Lead traveller

html
<form class="flux-card flux-wizard" data-flux-wizard aria-labelledby="wizard-title">
  <h2 class="flux-card__title" id="wizard-title">New group booking</h2>
  <ol class="flux-stepper" aria-label="Steps">
    <li class="flux-stepper__step" aria-current="step"><span class="flux-stepper__marker">1</span><span class="flux-stepper__label">Lead traveller</span></li>
    <li class="flux-stepper__step"><span class="flux-stepper__marker">2</span><span class="flux-stepper__label">Departure</span></li>
    <li class="flux-stepper__step"><span class="flux-stepper__marker">3</span><span class="flux-stepper__label">Review</span></li>
  </ol>
  <section class="flux-wizard__step" aria-labelledby="wizard-step-1">
    <h3 class="flux-wizard__title" id="wizard-step-1">Lead traveller</h3>
    <div class="flux-field" data-error="Enter the lead traveller’s full name.">
      <label class="flux-field__label" for="wizard-name">Full name <span class="flux-field__required" aria-hidden="true">*</span></label>
      <input class="flux-input" id="wizard-name" name="name" type="text" autocomplete="name" required />
    </div>
    <div class="flux-field" data-error="Enter an email address, for example anna@example.com.">
      <label class="flux-field__label" for="wizard-email">Email <span class="flux-field__required" aria-hidden="true">*</span></label>
      <input class="flux-input" id="wizard-email" name="email" type="email" autocomplete="email" required />
    </div>
  </section>
  <section class="flux-wizard__step" aria-labelledby="wizard-step-2" hidden>
    <h3 class="flux-wizard__title" id="wizard-step-2">Departure</h3>
    <fieldset class="flux-choice-group" data-error="Choose a departure.">
      <legend>Departure <span class="flux-field__required" aria-hidden="true">*</span></legend>
      <label class="flux-choice"><input class="flux-radio" type="radio" name="departure" value="GC-0914" required /> Golden Circle Classic, 14 Sep</label>
      <label class="flux-choice"><input class="flux-radio" type="radio" name="departure" value="SC-0919" required /> South Coast Explorer, 19 Sep</label>
      <label class="flux-choice"><input class="flux-radio" type="radio" name="departure" value="SN-0922" required /> Snæfellsnes Peninsula, 22 Sep</label>
    </fieldset>
    <div class="flux-field" data-error="Enter between 1 and 49 travellers.">
      <label class="flux-field__label" for="wizard-pax">Travellers <span class="flux-field__required" aria-hidden="true">*</span></label>
      <input class="flux-input" id="wizard-pax" name="pax" type="number" inputmode="numeric" min="1" max="49" required />
    </div>
  </section>
  <section class="flux-wizard__step" aria-labelledby="wizard-step-3" hidden>
    <h3 class="flux-wizard__title" id="wizard-step-3">Review</h3>
    <dl class="flux-dl">
      <div class="flux-dl__pair"><dt class="flux-dl__term">Lead traveller</dt><dd class="flux-dl__detail" data-flux-review="name"></dd></div>
      <div class="flux-dl__pair"><dt class="flux-dl__term">Email</dt><dd class="flux-dl__detail" data-flux-review="email"></dd></div>
      <div class="flux-dl__pair"><dt class="flux-dl__term">Departure</dt><dd class="flux-dl__detail" data-flux-review="departure"></dd></div>
      <div class="flux-dl__pair"><dt class="flux-dl__term">Travellers</dt><dd class="flux-dl__detail flux-dl__detail--data" data-flux-review="pax"></dd></div>
    </dl>
  </section>
  <div class="flux-wizard__actions">
    <button class="flux-button flux-button--secondary" type="button" data-flux-wizard-back hidden>Back</button>
    <button class="flux-button" type="button" data-flux-wizard-next>Next</button>
    <button class="flux-button" type="submit" data-flux-wizard-submit hidden>Create booking</button>
  </div>
</form>

Anatomy

PartDescription
StepperAn ordered list labelled for the task.
StepA marker and a label. aria-current="step" on the current one; data-state="complete" or "error" on others.
MarkerA near-square box with the number in mono, a tick when complete, or an alert mark on error.
ConnectorA hairline after each step that turns teal once the step is complete.
Wizard stepA section with a heading per step. Only the current one is shown.
ActionsBack on the left, Next or the final submit on the right, below a hairline.

API

NameKindDescription
flux-stepperclassOn the ol. Label it for the task.
flux-stepper__stepclassOn each li.
flux-stepper__markerclassThe number, or a tick or alert icon with hidden text.
flux-stepper__labelclassThe step name. Below 48rem only the current step keeps its visible label.
aria-current="step"attributeOn the current step.
data-stateattribute"complete" or "error" on a step that is not current.
data-flux-wizardattributeOn the form. Wires the steps, the stepper, validation and the review.
flux-wizard__stepclassOn each section. Hide all but the first in the markup.
flux-wizard__titleclassThe step heading. Receives focus when its step opens.
flux-wizard__actionsclassThe action row.
data-flux-wizard-back | -next | -submitattributeOn the three buttons. The script shows the right ones for each step.
data-errorattributeOn a flux-field or choice group: the message shown when its control is invalid. Without it the browser’s message is used.
data-flux-reviewattributeOn an element in the review: the name of the field whose value it shows. For radios, the checked option’s label.
flux-wizard-completeelementCancelable event dispatched on the form when every step is valid and it is submitted. Call preventDefault to submit by script instead.

States

StateTreatment
UpcomingA border-strong marker with the number in text-secondary.
CurrentA teal filled marker with the number in text-on-color; the label is bold text-primary.
CompleteA teal outlined marker with a tick, and a teal connector after it.
ErrorAn error outlined marker with an alert mark, and the label in text-error.
Invalid fieldaria-invalid on the control and an error message below it, bound with aria-describedby. The message clears once the value is valid.

Keyboard

KeysAction
TabMoves through the fields of the current step, then Back and Next.
EnterIn a field, submits the form, which validates every step and opens the first one with a problem.

Accessibility

  • The stepper is a list, so its length and each position are announced; aria-current marks the current step.
  • Completed and error markers carry hidden text, so the icon and colour are not the only signal.
  • Moving to a step focuses its heading, so screen reader users hear where they are.
  • An invalid step keeps focus on its first invalid field, with the message bound to it.

Tokens

The tier-3 and tier-2 tokens this component binds to. Change the token, not the component.

  • --flux-stepper-marker-size | -gap
  • --flux-stepper-connector | -connector-complete
  • --flux-stepper-marker-border | -marker-text
  • --flux-stepper-marker-current-bg | -marker-current-text
  • --flux-stepper-marker-complete | -marker-error
  • --flux-stepper-label | -label-current

Do and don’t

Do

  • Keep a wizard to three to six steps, each a coherent group of fields.
  • End on a review that reads back what will be submitted.
  • Keep answers when someone goes back a step.

Don’t

  • Do not make the steps clickable when they must be done in order.
  • Do not validate a step before someone tries to leave it.
  • Do not use a wizard for a form that fits on one screen.