Stepper

Part of the extras bundle, not included by default.

Intent

A horizontal sequence of numbered steps that tells the user where they are in a multi-screen flow -- account creation, checkout, onboarding wizards, settings migrations. State rides on badge color (success for done, accent for current, neutral for upcoming) and on aria-current="step" for the active step's label weight.

The stepper is mostly composition reuse: .cluster handles the horizontal layout, .badge.as-circle paints the numbered indicator, and .divider + a stepper-specific .stepper-connector modifier fills the gap between steps. The stepper file just adds a thin layer of glue.

Basic usage

<nav class="cluster stepper" aria-label="Progress">
    <span class="step">
        <span class="badge as-circle is-success" aria-label="Complete">✓</span>
        <span class="step-label">Account</span>
    </span>
    <hr class="divider stepper-connector"/>
    <span class="step" aria-current="step">
        <span class="badge as-circle is-accent">2</span>
        <span class="step-label">Profile</span>
    </span>
    <hr class="divider stepper-connector"/>
    <span class="step is-upcoming">
        <span class="badge as-circle is-neutral">3</span>
        <span class="step-label">Preferences</span>
    </span>
    <hr class="divider stepper-connector"/>
    <span class="step is-upcoming">
        <span class="badge as-circle is-neutral">4</span>
        <span class="step-label">Review</span>
    </span>
</nav>

The active step takes aria-current="step"; brio auto-bolds its .step-label via the attribute selector. Upcoming steps add .is-upcoming on the .step to mute the label color.

Checkmarks for completed steps

Replace the step number with a checkmark glyph for completed steps. Pair the badge with aria-label="Complete" so assistive tech announces what the symbol means.

<nav class="cluster stepper" aria-label="Progress">
    <span class="step">
        <span class="badge as-circle is-success" aria-label="Complete">✓</span>
        <span class="step-label">Account</span>
    </span>
    <hr class="divider stepper-connector"/>
    <span class="step">
        <span class="badge as-circle is-success" aria-label="Complete">✓</span>
        <span class="step-label">Profile</span>
    </span>
    <hr class="divider stepper-connector"/>
    <span class="step" aria-current="step">
        <span class="badge as-circle is-accent">3</span>
        <span class="step-label">Preferences</span>
    </span>
    <hr class="divider stepper-connector"/>
    <span class="step is-upcoming">
        <span class="badge as-circle is-neutral">4</span>
        <span class="step-label">Review</span>
    </span>
</nav>

Double-digit steps

The badge's as-circle shape stretches into a pill when content widens past one character -- the circle becomes an oval gracefully. No special markup needed.

<nav class="cluster stepper" aria-label="Progress">
    <span class="step">
        <span class="badge as-circle is-success" aria-label="Complete">✓</span>
        <span class="step-label">Intro</span>
    </span>
    <hr class="divider stepper-connector"/>
    <span class="step">
        <span class="badge as-circle is-success" aria-label="Complete">✓</span>
        <span class="step-label">Details</span>
    </span>
    <hr class="divider stepper-connector"/>
    <span class="step" aria-current="step">
        <span class="badge as-circle is-accent">10</span>
        <span class="step-label">Review</span>
    </span>
    <hr class="divider stepper-connector"/>
    <span class="step is-upcoming">
        <span class="badge as-circle is-neutral">11</span>
        <span class="step-label">Payment</span>
    </span>
    <hr class="divider stepper-connector"/>
    <span class="step is-upcoming">
        <span class="badge as-circle is-neutral">12</span>
        <span class="step-label">Confirm</span>
    </span>
</nav>

Always-compact (badges only)

.stepper.is-compact hides every .step-label at every width -- useful in card headers, modal toolbars, mobile layouts where labels would overrun the available space. Move the step name into an aria-label on each badge so assistive tech still hears it.

<nav class="cluster stepper is-compact" aria-label="Progress">
    <span class="step">
        <span class="badge as-circle is-success" aria-label="Step 1, Account, complete">✓</span>
        <span class="step-label">Account</span>
    </span>
    <hr class="divider stepper-connector"/>
    <span class="step" aria-current="step">
        <span class="badge as-circle is-accent" aria-label="Step 2, Profile, current">2</span>
        <span class="step-label">Profile</span>
    </span>
    <hr class="divider stepper-connector"/>
    <span class="step is-upcoming">
        <span class="badge as-circle is-neutral" aria-label="Step 3, Review, upcoming">3</span>
        <span class="step-label">Review</span>
    </span>
</nav>

Condensed (responsive collapse)

.stepper.is-condensed sets container-type: inline-size on the <nav> and a container query collapses labels at widths below 30rem. Wide steppers keep their labels; narrow embeds (sidebars, narrow cards) auto-compact. Resize the demo below to see the collapse.

<docs-resize>
    <nav class="cluster stepper is-condensed" aria-label="Progress">
        <span class="step">
            <span class="badge as-circle is-success" aria-label="Step 1, Account, complete">✓</span>
            <span class="step-label">Account</span>
        </span>
        <hr class="divider stepper-connector"/>
        <span class="step">
            <span class="badge as-circle is-success" aria-label="Step 2, Profile, complete">✓</span>
            <span class="step-label">Profile</span>
        </span>
        <hr class="divider stepper-connector"/>
        <span class="step" aria-current="step">
            <span class="badge as-circle is-accent" aria-label="Step 3, Preferences, current">3</span>
            <span class="step-label">Preferences</span>
        </span>
        <hr class="divider stepper-connector"/>
        <span class="step is-upcoming">
            <span class="badge as-circle is-neutral" aria-label="Step 4, Review, upcoming">4</span>
            <span class="step-label">Review</span>
        </span>
    </nav>
</docs-resize>

Container query lengths can't be var()-driven, so the threshold isn't tokenized. Override the rule in your own CSS:

/* In your project CSS, after brio-extras.css */
@container (max-inline-size: 40rem) {
    .stepper.is-condensed .step-label {
        display: none;
    }
    .stepper.is-condensed .stepper-connector {
        min-inline-size: var(--space-sm);
    }
}

Customization

Scoped tokens drive the stepper's geometry: --stepper-gap (between badge and label inside a step) and --stepper-connector-min (the minimum width of each divider connector). Override on the .stepper root for tighter or looser layouts. State color comes from the badge's color variants -- override --badge-bg / --badge-color on the badge itself if a non-standard tint is needed.

How it works

The stepper sets --cluster-wrap: nowrap on the .cluster root so steps stay on one line. .stepper-connector is a divider modifier with flex: 1 (so it grows to fill the space between steps) and zeroed margins (so the divider's default --divider-spacing doesn't bleed into the cluster's vertical rhythm).

The active step gets aria-current="step"; brio binds visual treatment (label weight) to the attribute, not a class -- consistent with breadcrumb's aria-current="page" and pagination's aria-current="page" wiring.

Accessibility

  • Wrap in <nav aria-label="Progress"> so the landmark is discoverable to assistive tech.
  • Mark the active step with aria-current="step".
  • For checkmarked steps, add aria-label="Complete" on the badge so the symbol's meaning reaches screen readers.
  • In .is-compact or when .is-condensed is collapsed, labels are display: none and removed from the a11y tree. Move the step name onto each badge's aria-label -- "Step 2, Profile, current" captures the position, name, and state in one announcement.

Markup

<nav class="cluster stepper" aria-label="Progress">
    <span class="step">
        <span class="badge as-circle is-success" aria-label="Complete">&check;</span>
        <span class="step-label">Account</span>
    </span>
    <hr class="divider stepper-connector">
    <span class="step" aria-current="step">
        <span class="badge as-circle is-accent">2</span>
        <span class="step-label">Profile</span>
    </span>
    <hr class="divider stepper-connector">
    <span class="step is-upcoming">
        <span class="badge as-circle is-neutral">3</span>
        <span class="step-label">Review</span>
    </span>
</nav>

<!-- Always-compact: add aria-label on each badge -->
<nav class="cluster stepper is-compact" aria-label="Progress"> ... </nav>

<!-- Auto-collapse: container-query at 30rem -->
<nav class="cluster stepper is-condensed" aria-label="Progress"> ... </nav>

CSS Reference: Stepper

Numbered step indicator for multi-step flows

Source: src/css/extras/stepper.css

Tokens

TokenDefaultDescription
--stepper-gapvar(--gap-snug)Gap between the badge and label inside each step
--stepper-connector-minvar(--space-lg)Minimum inline size of the divider connector between steps
--cluster-wrapnowrapFlex-wrap override for the cluster composition

Slots

SlotDescription
.stepper .stepStep pair containing a badge and optional label
.stepper-connectorDivider connector line between steps

Classes

ClassDescription
.steppercomponent root
.is-compactAlways hides labels, showing badges only at every width
.is-condensedAuto-hides labels at narrow container widths via container query