SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/steppers

8 CSS Steppers

7 Pure CSS1 with JSPublished 2026-10-04Free · no sign-up

Eight step indicators for checkouts, onboarding and order tracking: a numbered horizontal stepper, a vertical checklist, a progress line with dots, a working multi-step form, an icon tracker, compact dots, clickable steps and a numbered How it works section.

01. Horizontal Checkout Stepper

StepsCheckout

A numbered stepper across the top of a checkout: completed steps show a tick, the current step is filled and labelled with aria-current="step", and upcoming steps are outlined. The connecting lines are pseudo-elements, so the list stays simple.

View code
<ol class="sth">
  <li class="done"><span>1</span>Cart</li>
  <li class="done"><span>2</span>Details</li>
  <li aria-current="step"><span>3</span>Shipping</li>
  <li><span>4</span>Payment</li>
</ol>
.sth { box-sizing: border-box; max-width: 100%; width: 460px; display: flex; margin: 0; padding: 0; list-style: none; font: 600 13px Inter, system-ui, sans-serif; color: #5d6679; }
.sth li { position: relative; flex: 1; display: grid; justify-items: center; gap: 6px; text-align: center; }
.sth li + li::before { content: ""; position: absolute; top: 15px; right: 50%; width: 100%; height: 2px; background: #d0d5dd; z-index: 0; }
.sth li.done + li::before, .sth li.done + li.done::before { background: #16a34a; }
.sth span { position: relative; z-index: 1; display: grid; place-items: center; width: 32px; height: 32px; border: 2px solid #d0d5dd; border-radius: 50%; background: #fff; color: #5d6679; }
.sth .done span { border-color: #16a34a; background: #16a34a; color: transparent; }
.sth .done span::after { content: "\2713"; position: absolute; color: #fff; }
.sth [aria-current] { color: #101828; }
.sth [aria-current] span { border-color: #2563eb; background: #2563eb; color: #fff; }

02. Vertical Progress Stepper

StepsOnboarding

A vertical stepper for onboarding checklists, with a title and a short description for each step joined by a line down the left. The current step's description is brighter, and finished steps show a tick both visually and in hidden text.

View code
<ol class="stv">
  <li class="done"><b>Create your shop<span class="sr"> (done)</span></b><p>Name, logo and currency.</p></li>
  <li class="done"><b>Add a product<span class="sr"> (done)</span></b><p>Photos, price and stock.</p></li>
  <li aria-current="step"><b>Connect payments</b><p>Link a payout account to get paid.</p></li>
  <li><b>Go live</b><p>Choose a domain and publish.</p></li>
</ol>
.stv { box-sizing: border-box; max-width: 100%; width: 320px; margin: 0; padding: 0; list-style: none; font-family: Inter, system-ui, sans-serif; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.stv li { position: relative; padding: 0 0 22px 36px; color: #8b95a9; }
.stv li::before { content: ""; position: absolute; left: 0; top: 0; width: 20px; height: 20px; box-sizing: border-box; border: 2px solid #3a4256; border-radius: 50%; background: #0e1118; }
.stv li:not(:last-child)::after { content: ""; position: absolute; left: 9px; top: 22px; bottom: 2px; width: 2px; background: #2c3346; }
.stv .done::before { border-color: #34d399; background: #34d399 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-6.5' fill='none' stroke='%230e1118' stroke-width='2.4'/%3E%3C/svg%3E") center / 12px no-repeat; }
.stv .done::after { background: #34d399 !important; }
.stv [aria-current]::before { border-color: #7c9cff; box-shadow: 0 0 0 4px rgba(124,156,255,.25); }
.stv b { display: block; color: #e7ebf5; font-size: 15px; }
.stv p { margin: 3px 0 0; font-size: 13.5px; }
.stv [aria-current] p { color: #c9d1e2; }

03. Stepper with Progress Line

StepsProgress

Dots on a single progress line that fills up to the current step, with labels underneath. The fill is one gradient-free bar whose width comes from a custom property, so changing --p moves the progress without touching the markup.

View code
<div class="spl" style="--p:66%">
  <p class="sr">Step 3 of 4: Review</p>
  <div class="line" aria-hidden="true"><i></i></div>
  <ol aria-hidden="true"><li class="on">Upload</li><li class="on">Edit</li><li class="on now">Review</li><li>Publish</li></ol>
</div>
.spl { box-sizing: border-box; max-width: 100%; width: 440px; font: 600 13px Inter, system-ui, sans-serif; color: #5d6679; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.line { position: relative; height: 4px; margin: 0 6% 14px; border-radius: 2px; background: #e4e7ec; }
.line i { position: absolute; inset: 0 auto 0 0; width: var(--p); border-radius: 2px; background: #7c3aed; }
.spl ol { display: grid; grid-template-columns: repeat(4, 1fr); margin: 0; padding: 0; list-style: none; text-align: center; }
.spl li { position: relative; padding-top: 6px; }
.spl li::before { content: ""; position: absolute; top: -26px; left: 50%; width: 14px; height: 14px; border: 3px solid #e4e7ec; border-radius: 50%; background: #fff; transform: translateX(-50%); }
.spl li.on::before { border-color: #7c3aed; background: #7c3aed; }
.spl li.now { color: #101828; }
.spl li.now::before { background: #fff; }

04. Multi-Step Form Stepper

FormJavaScript

A three-step sign-up form where the stepper at the top updates as you move between steps with Back and Next. Each step is a fieldset with a legend, the progress text is a live region, and focus moves to the new step's first field.

View code
<form class="msf" onsubmit="event.preventDefault()">
  <p class="pg" aria-live="polite">Step <b id="msf-n">1</b> of 3</p>
  <div class="bar" aria-hidden="true"><i id="msf-b"></i></div>
  <fieldset data-s="1"><legend>Your name</legend><input aria-label="Full name" autocomplete="name"></fieldset>
  <fieldset data-s="2" hidden><legend>Your email</legend><input aria-label="Email" type="email" autocomplete="email"></fieldset>
  <fieldset data-s="3" hidden><legend>Choose a password</legend><input aria-label="Password" type="password" autocomplete="new-password"></fieldset>
  <div class="nav"><button type="button" id="msf-back" disabled>Back</button><button type="button" id="msf-next">Next</button></div>
</form>
.msf { box-sizing: border-box; max-width: 100%; width: 340px; display: grid; gap: 12px; padding: 22px; border: 1px solid #262d3e; border-radius: 16px; background: #12161f; color: #c9d1e2; font: 14px Inter, system-ui, sans-serif; }
.pg { margin: 0; }
.bar { height: 6px; border-radius: 3px; background: #262d3e; }
.bar i { display: block; height: 100%; width: 33%; border-radius: 3px; background: #22d3ee; transition: width .3s; }
.msf fieldset { margin: 0; padding: 0; border: 0; min-inline-size: 0; }
.msf legend { margin-bottom: 8px; color: #fff; font-size: 17px; font-weight: 700; }
.msf input { box-sizing: border-box; width: 100%; padding: 11px 12px; border: 1px solid #343c51; border-radius: 10px; background: #0d1018; color: #fff; font-size: 15px; outline: none; }
.msf input:focus { border-color: #22d3ee; }
.nav { display: flex; justify-content: space-between; }
.nav button { padding: 9px 16px; border: 1px solid #343c51; border-radius: 10px; background: #1a1f2c; color: #fff; font: 700 14px Inter, sans-serif; cursor: pointer; }
.nav button:disabled { opacity: .4; cursor: default; }
#msf-next { border: 0; background: #22d3ee; color: #062a33; }
.nav button:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .bar i { transition: none; } }
let step = 1;
const sets = [...document.querySelectorAll('.msf fieldset')], back = document.getElementById('msf-back'), next = document.getElementById('msf-next');
function show() {
  sets.forEach(f => f.hidden = +f.dataset.s !== step);
  document.getElementById('msf-n').textContent = step;
  document.getElementById('msf-b').style.width = (step / 3 * 100) + '%';
  back.disabled = step === 1; next.textContent = step === 3 ? 'Create account' : 'Next';
  sets[step - 1].querySelector('input').focus();
}
next.addEventListener('click', () => { if (step < 3) { step++; show(); } });
back.addEventListener('click', () => { if (step > 1) { step--; show(); } });

05. Icon Stepper for Order Tracking

TrackingIcons

An order tracking timeline with an icon for each stage: ordered, packed, shipped and delivered. The reached stages are coloured, each stage has a date underneath, and the current stage is announced with aria-current and plain text.

View code
<ol class="sti">
  <li class="ok"><span aria-hidden="true">&#128722;</span><b>Ordered</b><small>2 Oct</small></li>
  <li class="ok"><span aria-hidden="true">&#128230;</span><b>Packed</b><small>3 Oct</small></li>
  <li class="ok" aria-current="step"><span aria-hidden="true">&#128666;</span><b>Shipped</b><small>4 Oct</small></li>
  <li><span aria-hidden="true">&#127968;</span><b>Delivered</b><small>Expected 6 Oct</small></li>
</ol>
.sti { box-sizing: border-box; max-width: 100%; width: 460px; display: grid; grid-template-columns: repeat(4, 1fr); margin: 0; padding: 0; list-style: none; font-family: Inter, system-ui, sans-serif; text-align: center; }
.sti li { position: relative; display: grid; justify-items: center; gap: 4px; }
.sti li + li::before { content: ""; position: absolute; top: 21px; right: 50%; width: 100%; height: 3px; background: #e4e7ec; }
.sti li.ok + li.ok::before { background: #0d9488; }
.sti span { position: relative; display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%; background: #f2f4f7; font-size: 20px; filter: grayscale(1); }
.sti .ok span { background: #ccfbf1; filter: none; }
.sti [aria-current] span { box-shadow: 0 0 0 4px #99f6e4; }
.sti b { color: #101828; font-size: 13px; }
.sti small { color: #5d6679; font-size: 12px; }

06. Compact Dots Stepper

MobileSteps

A small stepper for tight spaces like mobile sheets: a row of dots where the current one stretches into a pill, plus Step 2 of 5 in text. The stretch animates with width, and the text carries the information for screen readers.

View code
<div class="stc">
  <ol aria-hidden="true"><li class="d"></li><li class="c"></li><li></li><li></li><li></li></ol>
  <p>Step 2 of 5</p>
</div>
.stc { box-sizing: border-box; max-width: 100%; display: flex; align-items: center; gap: 16px; font: 600 14px Inter, system-ui, sans-serif; color: #c9d1e2; }
.stc ol { display: flex; gap: 6px; margin: 0; padding: 0; list-style: none; }
.stc li { width: 8px; height: 8px; border-radius: 4px; background: #343c51; transition: width .25s; }
.stc li.d { background: #a78bfa; }
.stc li.c { width: 26px; background: #a78bfa; }
.stc p { margin: 0; }

07. Clickable Step Tabs

NavigationSteps

A stepper where completed steps are links back to earlier pages, the current step is plain text and future steps are disabled. It is a nav landmark with an ordered list, which is the right structure when steps are separate pages.

View code
<nav class="stk" aria-label="Application progress">
  <ol>
    <li><a href="#"><span>&#10003;</span>Personal</a></li>
    <li><a href="#"><span>&#10003;</span>Address</a></li>
    <li><span class="cur" aria-current="step"><span>3</span>Documents</span></li>
    <li><span class="fut"><span>4</span>Review</span></li>
  </ol>
</nav>
.stk { box-sizing: border-box; max-width: 100%; font: 600 13.5px Inter, system-ui, sans-serif; }
.stk ol { display: flex; flex-wrap: wrap; gap: 8px; margin: 0; padding: 0; list-style: none; }
.stk a, .stk .cur, .stk .fut { display: inline-flex; align-items: center; gap: 8px; padding: 8px 12px 8px 8px; border-radius: 999px; text-decoration: none; }
.stk a { background: #ecfdf3; color: #054f31; }
.stk a:hover { background: #d1fadf; }
.stk a:focus-visible { outline: 2px solid #079455; outline-offset: 2px; }
.stk .cur { background: #101828; color: #fff; }
.stk .fut { background: #f2f4f7; color: #5d6679; }
.stk ol span span, .stk a span { display: grid; place-items: center; width: 22px; height: 22px; border-radius: 50%; background: rgba(255,255,255,.6); font-size: 12px; }
.stk a span { background: #079455; color: #fff; }
.stk .cur span { background: #fff; color: #101828; }

08. Large Numbered Steps

MarketingHow it works

A How it works section with big outlined numbers and a short explanation for each step, arranged in a responsive grid. It is an ordered list, so the numbering is meaningful to assistive technology as well as visible.

View code
<ol class="stn">
  <li><h3>Pick a template</h3><p>Start from a layout built for your kind of shop.</p></li>
  <li><h3>Add your products</h3><p>Upload photos and set prices in minutes.</p></li>
  <li><h3>Start selling</h3><p>Share your link and take your first order.</p></li>
</ol>
.stn { box-sizing: border-box; max-width: 100%; width: 560px; display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 18px; margin: 0; padding: 0; list-style: none; counter-reset: s; font-family: Inter, system-ui, sans-serif; }
.stn li { counter-increment: s; }
.stn li::before { content: "0" counter(s); display: block; margin-bottom: 6px; color: transparent; font-size: 44px; font-weight: 900; -webkit-text-stroke: 1.5px #7c9cff; }
.stn h3 { margin: 0 0 4px; color: #fff; font-size: 16px; }
.stn p { margin: 0; color: #aab3c7; font-size: 14px; line-height: 1.5; }

About these CSS Steppers

Mark the current step

Steppers are ordered lists. Mark the current step with aria-current="step" and write finished steps as done in text, not only with a tick, so screen readers can tell where the person is.

Lines between steps

The connecting lines are pseudo-elements on each item after the first, positioned behind the step circles. Colouring the line from completed steps shows progress without extra elements.

Say Step 3 of 5

Compact designs such as dots should always include text like Step 2 of 5. Dots alone carry no meaning for assistive technology or for people who cannot tell the colours apart.

Steps as pages

When each step is its own page, make completed steps links so people can go back, keep the current one as text, and leave future steps unlinked until they are reachable.

Frequently asked questions

Do steppers need JavaScript?

Only the multi-step form, to move between steps. The rest are HTML and CSS.

Should future steps be clickable?

Usually not, because the earlier information is needed first. Let people go back, not skip ahead.

How many steps are too many?

Keep checkouts to three to five steps; long forms are easier when grouped into a few clear stages.