ExtrasStepper

A progress list for multi-step flows, horizontal, vertical or responsive, with the current step in aria-current.

On this page

A stepper is an ol with one hook, data-stepper. The current step is aria-current="step". Steps before it read as completed, steps after it as upcoming. No class is needed, and no script, unless you want to move the current step.

Load css/extras/stepper.min.css, or the whole set from css/pico.extras.min.css. See the overview.

Horizontal

The marker is a span with aria-hidden="true" before the label text. The value of data-stepper is horizontal (the default), vertical or responsive. Put the status in the label text for screen readers, like "Build, completed".

Checkout progress
<ol data-stepper="horizontal" aria-label="Checkout progress">
  <li><span aria-hidden="true">1</span>Cart</li>
  <li><span aria-hidden="true">2</span>Shipping</li>
  <li aria-current="step"><span aria-hidden="true">3</span>Payment</li>
  <li><span aria-hidden="true">4</span>Review</li>
</ol>

When steps are links, wrap the list in a nav and put aria-current on the link. A step the user cannot reach yet is a link without href, or with aria-disabled="true".

Onboarding steps
<nav aria-label="Onboarding">
  <ol data-stepper>
    <li>
      <a href="#account"><span aria-hidden="true">1</span>Account</a>
    </li>
    <li>
      <a href="#workspace"><span aria-hidden="true">2</span>Workspace</a>
    </li>
    <li>
      <a href="#team" aria-current="step"><span aria-hidden="true">3</span>Invite team</a>
    </li>
    <li>
      <a aria-disabled="true"><span aria-hidden="true">4</span>Billing</a>
    </li>
  </ol>
</nav>

Vertical

Order status
<ol data-stepper="vertical" aria-label="Order status">
  <li><span aria-hidden="true">1</span>Order placed</li>
  <li><span aria-hidden="true">2</span>Payment confirmed</li>
  <li aria-current="step"><span aria-hidden="true">3</span>Packed and ready to ship</li>
  <li><span aria-hidden="true">4</span>Out for delivery</li>
  <li><span aria-hidden="true">5</span>Delivered</li>
</ol>

Responsive

data-stepper="responsive" is horizontal from 576px and vertical below it. Resize the window to see it.

Responsive stepper
<ol data-stepper="responsive" aria-label="Plan setup">
  <li><span aria-hidden="true">1</span>Choose plan</li>
  <li><span aria-hidden="true">2</span>Billing details</li>
  <li aria-current="step"><span aria-hidden="true">3</span>Add seats</li>
  <li><span aria-hidden="true">4</span>Confirm and pay</li>
</ol>

In a card

Stepper in a card
<article>
  <header>
    <ol data-stepper aria-label="Setup" style="margin-bottom: 0">
      <li><span aria-hidden="true">1</span>Connect</li>
      <li aria-current="step"><span aria-hidden="true">2</span>Configure</li>
      <li><span aria-hidden="true">3</span>Launch</li>
    </ol>
  </header>
  <p>Choose the region and the retention period for your logs.</p>
  <footer>
    <button class="secondary">Back</button>
    <button>Continue</button>
  </footer>
</article>

Moving the current step

The CSS follows aria-current, so advancing a step is one attribute. This helper moves it, on the link when the steps are links.

JavaScript
function setStep(list, index) {
  list.querySelectorAll(":scope > li").forEach((li, i) => {
    const el = li.querySelector("a") ?? li;
    if (i === index) el.setAttribute("aria-current", "step");
    else el.removeAttribute("aria-current");
  });
}

Accessibility

  • Give the list a name with aria-label, or put it in a labelled nav.
  • aria-current="step" tells assistive technology which step is active. Completed and upcoming states are only visual, so say them in the text when it matters.
  • The number marker is aria-hidden, because the list already numbers its items.
  • Links have a visible focus ring, and a disabled link is dimmed and ignores the pointer.

Customization

--pico-stepper-marker-size
2rem
Diameter of the number circle.
--pico-stepper-line-width
0.125rem
Thickness of the line between steps.
--pico-stepper-gap
0.5rem
Space between marker and label.
--pico-stepper-ring-width
var(--pico-outline-width)
Width of the ring around the current marker.
--pico-stepper-line-color
var(--pico-muted-border-color)
Line and outline of upcoming steps.
--pico-stepper-label-color
var(--pico-muted-color)
Label color of upcoming and completed steps.
--pico-stepper-current-color
var(--pico-color)
Label color of the current step.
--pico-stepper-current-ring-color
var(--pico-primary-background)
Ring around the current marker.
--pico-stepper-complete-background
var(--pico-primary-background)
Marker fill of completed steps and the current step.
--pico-stepper-complete-color
var(--pico-primary-inverse)
Number color on a filled marker.
--pico-stepper-complete-line-color
var(--pico-primary-background)
Line after a completed step.