Steps
Steps show where the user is in an ordered, multi-step flow (checkout,
onboarding, a wizard). It is a native <ol class="re-steps"> of
<li class="re-steps__step">, each carrying data-status — complete,
current, or upcoming. It works with zero JavaScript: markers auto-number
via a CSS counter, a complete step swaps its number for a pure-CSS check, and the
connecting rail is drawn entirely in CSS.
It is a display indicator, not an ARIA tablist or navigation widget — there
is no roving tabindex and no panel switching. The ordered-list semantics are kept
(so assistive tech announces “2 of 4”); the list is stripped visually in CSS
rather than with role="list", which would discard that ordinal. The current
step carries aria-current="step" on its <li>.
Vertical
Section titled “Vertical”- Completed: Account Email and password
- Completed: Profile Name and avatar
- Billing Payment method
- Upcoming: Review Confirm your order
<ol class="re-steps" data-orientation="vertical">
<li class="re-steps__step" data-status="complete">
<a class="re-steps__content" href="#account">
<span class="re-sr-only">Completed: </span>
<span class="re-steps__marker" aria-hidden="true"></span>
<span class="re-steps__text">
<span class="re-steps__title">Account</span>
<span class="re-steps__description">Email and password</span>
</span>
</a>
</li>
<li class="re-steps__step" data-status="complete">
<a class="re-steps__content" href="#profile">
<span class="re-sr-only">Completed: </span>
<span class="re-steps__marker" aria-hidden="true"></span>
<span class="re-steps__text">
<span class="re-steps__title">Profile</span>
<span class="re-steps__description">Name and avatar</span>
</span>
</a>
</li>
<li class="re-steps__step" data-status="current" aria-current="step">
<span class="re-steps__content">
<span class="re-steps__marker" aria-hidden="true"></span>
<span class="re-steps__text">
<span class="re-steps__title">Billing</span>
<span class="re-steps__description">Payment method</span>
</span>
</span>
</li>
<li class="re-steps__step" data-status="upcoming">
<span class="re-steps__content">
<span class="re-sr-only">Upcoming: </span>
<span class="re-steps__marker" aria-hidden="true"></span>
<span class="re-steps__text">
<span class="re-steps__title">Review</span>
<span class="re-steps__description">Confirm your order</span>
</span>
</span>
</li>
</ol> Horizontal
Section titled “Horizontal”data-orientation="horizontal" flows the steps inline with the rail behind the
markers.
- Completed: Plan Choose a tier
- Build Set it up
- Upcoming: Ship Go live
<ol class="re-steps" data-orientation="horizontal">
<li class="re-steps__step" data-status="complete">
<a class="re-steps__content" href="#plan">
<span class="re-sr-only">Completed: </span>
<span class="re-steps__marker" aria-hidden="true"></span>
<span class="re-steps__text">
<span class="re-steps__title">Plan</span>
<span class="re-steps__description">Choose a tier</span>
</span>
</a>
</li>
<li class="re-steps__step" data-status="current" aria-current="step">
<span class="re-steps__content">
<span class="re-steps__marker" aria-hidden="true"></span>
<span class="re-steps__text">
<span class="re-steps__title">Build</span>
<span class="re-steps__description">Set it up</span>
</span>
</span>
</li>
<li class="re-steps__step" data-status="upcoming">
<span class="re-steps__content">
<span class="re-sr-only">Upcoming: </span>
<span class="re-steps__marker" aria-hidden="true"></span>
<span class="re-steps__text">
<span class="re-steps__title">Ship</span>
<span class="re-steps__description">Go live</span>
</span>
</span>
</li>
</ol> data-size="sm" and data-size="lg" scale the markers, gap, and type together
(mirroring progress); sm drops the
description line in horizontal layouts.
- Completed: Cart Your items
- Shipping Address
- Upcoming: Pay Card details
- Completed: Cart Your items
- Shipping Address
- Upcoming: Pay Card details
<ol class="re-steps" data-orientation="horizontal" data-size="sm">
<li class="re-steps__step" data-status="complete">
<span class="re-steps__content">
<span class="re-sr-only">Completed: </span>
<span class="re-steps__marker" aria-hidden="true"></span>
<span class="re-steps__text">
<span class="re-steps__title">Cart</span>
<span class="re-steps__description">Your items</span>
</span>
</span>
</li>
<li class="re-steps__step" data-status="current" aria-current="step">
<span class="re-steps__content">
<span class="re-steps__marker" aria-hidden="true"></span>
<span class="re-steps__text">
<span class="re-steps__title">Shipping</span>
<span class="re-steps__description">Address</span>
</span>
</span>
</li>
<li class="re-steps__step" data-status="upcoming">
<span class="re-steps__content">
<span class="re-sr-only">Upcoming: </span>
<span class="re-steps__marker" aria-hidden="true"></span>
<span class="re-steps__text">
<span class="re-steps__title">Pay</span>
<span class="re-steps__description">Card details</span>
</span>
</span>
</li>
</ol>
<ol class="re-steps" data-orientation="horizontal" data-size="lg">
<li class="re-steps__step" data-status="complete">
<span class="re-steps__content">
<span class="re-sr-only">Completed: </span>
<span class="re-steps__marker" aria-hidden="true"></span>
<span class="re-steps__text">
<span class="re-steps__title">Cart</span>
<span class="re-steps__description">Your items</span>
</span>
</span>
</li>
<li class="re-steps__step" data-status="current" aria-current="step">
<span class="re-steps__content">
<span class="re-steps__marker" aria-hidden="true"></span>
<span class="re-steps__text">
<span class="re-steps__title">Shipping</span>
<span class="re-steps__description">Address</span>
</span>
</span>
</li>
<li class="re-steps__step" data-status="upcoming">
<span class="re-steps__content">
<span class="re-sr-only">Upcoming: </span>
<span class="re-steps__marker" aria-hidden="true"></span>
<span class="re-steps__text">
<span class="re-steps__title">Pay</span>
<span class="re-steps__description">Card details</span>
</span>
</span>
</li>
</ol> - Set each step’s
data-statustocomplete,current, orupcoming, and putaria-current="step"on the same<li>asdata-status="current"(exactly once). With no JavaScript, advancing the flow is your framework re-rendering those attributes. - A completed step’s
.re-steps__contentmay be a real<a href>or<button>— it becomes focusable and shows a hover/focus affordance. Leave it a<span>for an inert step (upcoming steps should stay inert). - The rail tints accent up to the last complete step: a complete step colors the segment leading out of it toward the next step. For a continuous “percent done” bar, compose progress instead — steps is deliberately discrete.
- Markers carry
aria-hidden="true"— the number and check are visual only. Because of that, completion is not programmatically determinable from the marker, so expose it to assistive tech with a visually-hidden status word on each non-current step:<span class="re-sr-only">Completed: </span>(orUpcoming:). The current step is already announced viaaria-current. The current step is also distinguished without color (a bolder title, and a system-Highlightmarker ring under Windows High Contrast); the completed step by its check glyph. - Tune metrics by overriding
--re-steps-marker-size,--re-steps-gap,--re-steps-rail-color, etc. on the root or any subtree.
Accessibility
Section titled “Accessibility”- Keyboard — there is no widget keyboard model: steps is a display
indicator, not a tablist, so there’s no roving tabindex, arrow navigation, or
panel switching. The only focusable parts are completed steps whose
.re-steps__contentyou make a real<a href>or<button>— reached with Tab and activated with Enter (or Space for a<button>) like any native link/button. Upcoming and current steps stay inert<span>s. - Focus — interactive steps show the standard
:focus-visiblering. Vertical layouts use the global outer ring; horizontal layouts (a tight, scrollable row) swap to an inset ring so it never clips at a container edge. - Semantics — the native
<ol>/<li>ordering is preserved (the list is stripped in CSS, not withrole="list", which would discard the “2 of 4” ordinal). The current step carriesaria-current="step"on its<li>. Markers arearia-hidden="true"— the number and check are visual only — so completion is exposed to assistive tech with a visually-hidden status word (<span class="re-sr-only">Completed: </span>/Upcoming:) on each non-current step. - Notes — status is conveyed without relying on color: the current step has a
bolder title plus
aria-current, and a complete step is marked by its check glyph. Under Windows High Contrast (forced-colors) the current marker takes a systemHighlightring and the focus ring is re-established as a real outline. Marker and rail transitions are dropped underprefers-reduced-motion. See the accessibility guide for system-wide conventions.