Steps
Step indicators show progress through a multi-step process. Built with CSS counters for auto-numbering, auto-generated connectors, and ARIA attributes for accessibility.
Overview
Step indicators communicate progress through a multi-step process like checkout, registration, or onboarding. The nav.steps pattern uses CSS-only numbered circles, auto-generated connectors, and semantic HTML.
Key features:
- CSS counters for auto-numbering — no manual
<span>1</span>needed - Auto connectors between steps via
::afterpseudo-elements - Checkmark on completed steps (replaces counter number)
- ARIA support —
aria-current="step"for active,aria-labelfor the nav - Variants — labels below, vertical, small/large sizes
- Dark mode — automatic via VB color tokens
- Wizard integration — auto-syncs with
wizard.jswhen present
Basic Step Indicator
The default horizontal layout with numbered circles, labels beside each circle, and auto-generated connectors between steps. Uses <ol> for semantic ordered progression.
<nav class="steps" aria-label="Checkout progress"> <ol> <li data-completed>Shipping</li> <li aria-current="step">Payment</li> <li>Review</li> </ol></nav>
Interactive Steps
Completed steps can contain <a> links to allow users to navigate back to earlier steps. Future steps remain plain text (non-clickable). The interactive demo below lets you navigate between steps with Previous/Next buttons.
<nav class="steps" aria-label="Registration progress"> <ol> <li data-completed> <a href="#account">Account</a> </li> <li data-completed> <a href="#profile">Profile</a> </li> <li aria-current="step">Preferences</li> <li>Confirm</li> </ol></nav>
Labels Below
Use data-labels="below" to position labels below their circles. Connectors run between circles at circle height using absolute positioning. This variant works well for steps with longer labels.
<nav class="steps" data-labels="below" aria-label="Account setup"> <ol> <li data-completed>Account</li> <li aria-current="step">Details</li> <li>Preferences</li> <li>Review</li> </ol></nav>
Vertical Steps
Use data-direction="vertical" to stack steps vertically with downward connectors. Useful for sidebar navigation or when you have many steps that need more space.
<nav class="steps" data-direction="vertical" aria-label="Setup progress"> <ol> <li data-completed>Create account</li> <li data-completed>Verify email</li> <li aria-current="step">Set up profile</li> <li>Choose plan</li> <li>Get started</li> </ol></nav>
Size Variants
Use data-size="sm" for compact indicators or data-size="lg" for more prominent ones. The default size is 2rem.
Small
<nav class="steps" data-size="sm" aria-label="Progress"> <ol> <li data-completed>Step 1</li> <li aria-current="step">Step 2</li> <li>Step 3</li> </ol></nav>
Large
<nav class="steps" data-size="lg" aria-label="Progress"> <ol> <li data-completed>Step 1</li> <li aria-current="step">Step 2</li> <li>Step 3</li> </ol></nav>
With Wizard Forms
When used inside (or referenced by) a form[data-wizard], the wizard.js controller auto-syncs nav.steps state as the user navigates steps. Point the form to the nav using data-wizard-steps="#id", or place the nav.steps inside the form for auto-discovery.
The wizard automatically:
- Sets
data-completedon all steps before the current one - Sets
aria-current="step"on the current step - Clears attributes from future steps
- Hides conditional steps when they don't apply
<!-- wizard.js auto-syncs nav.steps when inside a data-wizard form --><form data-wizard data-wizard-steps="#checkout-steps"> <nav class="steps" id="checkout-steps" aria-label="Checkout progress"> <ol> <li>Shipping</li> <li>Payment</li> <li>Review</li> </ol> </nav> <progress data-wizard-progress max="3" value="1"></progress> <fieldset data-wizard-step> <legend>Shipping</legend> <!-- shipping fields --> </fieldset> <fieldset data-wizard-step> <legend>Payment</legend> <!-- payment fields --> </fieldset> <fieldset data-wizard-step> <legend>Review</legend> <!-- review content --> </fieldset> <nav data-wizard-nav> <button type="button" data-wizard-prev>Previous</button> <button type="button" data-wizard-next>Next</button> <button type="submit">Place order</button> </nav></form>
States
Each step can be in one of three states:
| State | Attribute | Circle | Label |
|---|---|---|---|
| Future (default) | none | Muted border, surface background | Muted text |
| Active | aria-current="step" |
Interactive background, white number | Bold text |
| Completed | data-completed |
Success background, checkmark | Normal text |
Variants
| Variant | Attribute | Behavior |
|---|---|---|
| Labels beside (default) | none | Circle + label in a row, connector stretches between |
| Labels below | data-labels="below" |
Circle on top, label below, connectors between circles |
| Vertical | data-direction="vertical" |
Steps stacked vertically, connector runs downward |
| Small | data-size="sm" |
1.5rem circles, smaller font |
| Large | data-size="lg" |
2.5rem circles, larger font |
CSS Variables
Override the private --_ variables on nav.steps to customize colors and sizes:
| Variable | Default | Purpose |
|---|---|---|
--_step-size |
2rem |
Circle diameter |
--_step-font |
var(--font-size-sm) |
Number/checkmark font size |
--_connector-height |
2px |
Connector line thickness |
--_connector-color |
var(--color-border) |
Default connector color |
--_connector-completed |
var(--color-success) |
Completed connector color |
--_active-bg |
var(--color-interactive) |
Active circle background |
--_completed-bg |
var(--color-success) |
Completed circle background |
--_future-bg |
var(--color-surface-raised) |
Future circle background |
/* Step indicator — included in VB core (nav/styles.css) */nav.steps { --_step-size: 2rem; --_step-font: var(--font-size-sm); --_connector-height: 2px; --_connector-color: var(--color-border); --_connector-completed: var(--color-success); --_future-bg: var(--color-surface-raised); --_future-border: var(--color-border); --_future-color: var(--color-text-muted); --_active-bg: var(--color-interactive); --_active-border: var(--color-interactive); --_active-text: white; --_completed-bg: var(--color-success); --_completed-border: var(--color-success); --_completed-text: white;} /* CSS counters auto-number circles */nav.steps > ol { counter-reset: step;}nav.steps li { counter-increment: step;}nav.steps li::before { content: counter(step); /* auto number */}nav.steps li[data-completed]::before { content: "\\2713"; /* checkmark replaces number */}
Accessibility
aria-current="step"on the active<li>announces the current step to screen readersaria-labelon the<nav>identifies the navigation purpose<ol>conveys ordered sequence to assistive technology- Completed steps with
<a>links are keyboard-navigable - When used with
wizard.js, step changes are announced via a live region - Future steps (no attributes) have muted visual treatment — not interactive, not focusable
Related
Wizard
Multi-step form patterns with wizard.js integration
Breadcrumb
Hierarchical location navigation
Pagination
Page-level navigation patterns
Nav Element
Full nav element documentation with all variants