Checkout step progress bar
An accessible step indicator for multi-page checkouts showing completed, current and upcoming steps with a connecting line.
Tailwind CSSFeedback and empty statesstepsprogresscheckoutstepper
Live preview
Code
<nav aria-label="Checkout progress" class="mx-auto max-w-2xl px-4 py-6">
<ol class="flex items-start">
<li data-state="done" class="group relative flex flex-1 flex-col items-center text-center">
<span class="absolute left-1/2 top-4 h-0.5 w-full bg-slate-200 group-data-[state=done]:bg-indigo-600" aria-hidden="true"></span>
<span class="relative z-10 flex size-8 items-center justify-center rounded-full bg-indigo-600 text-sm font-semibold text-white">
<svg class="size-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="m7.7 13.6-3.3-3.3-1.4 1.4 4.7 4.7L17 7.1l-1.4-1.4-7.9 7.9Z"/></svg>
</span>
<span class="mt-2 hidden text-xs font-medium text-slate-700 sm:block">Cart</span>
</li>
<li data-state="current" aria-current="step" class="group relative flex flex-1 flex-col items-center text-center">
<span class="absolute left-1/2 top-4 h-0.5 w-full bg-slate-200" aria-hidden="true"></span>
<span class="relative z-10 flex size-8 items-center justify-center rounded-full border-2 border-indigo-600 bg-white text-sm font-semibold text-indigo-600">2</span>
<span class="mt-2 text-xs font-semibold text-indigo-600">Shipping</span>
</li>
<li data-state="todo" class="group relative flex flex-1 flex-col items-center text-center">
<span class="absolute left-1/2 top-4 h-0.5 w-full bg-slate-200" aria-hidden="true"></span>
<span class="relative z-10 flex size-8 items-center justify-center rounded-full border-2 border-slate-300 bg-white text-sm font-semibold text-slate-400">3</span>
<span class="mt-2 hidden text-xs text-slate-500 sm:block">Payment</span>
</li>
<li data-state="todo" class="group relative flex flex-1 flex-col items-center text-center">
<span class="relative z-10 flex size-8 items-center justify-center rounded-full border-2 border-slate-300 bg-white text-sm font-semibold text-slate-400">4</span>
<span class="mt-2 hidden text-xs text-slate-500 sm:block">Review</span>
</li>
</ol>
</nav>
How it works
What it solves
People abandon long forms when they cannot tell how far along they are. A step indicator shows the route ahead, and when it is built as a real ordered list it also tells assistive technology which step is current.
How it works
The steps are an ol with one li per stage. State is expressed with data attributes (data-state="done", "current" or "todo"), which Tailwind targets through data-[state=done]: variants for the circle colour and label weight. The current item also gets aria-current="step". Completed circles show a check mark SVG, while the others show their number. The connecting line is a pseudo element drawn by an absolutely positioned span between circles, coloured by the state of the step it leads out of, so progress reads left to right. Labels hide below sm except the current one, which keeps the row inside narrow phones without wrapping.
Adapting it
Render the list from your step array and set the state by comparing each index to the current one. Links can wrap completed steps so people can go back, but leave future steps as plain text. Add a fourth step by copying one li.
Pitfalls
- Do not use colour as the only signal; the check mark and number carry meaning too.
- Only the current step may have
aria-current. - Skipped steps should not look completed.
How to use it
- Paste
checkout-steps.htmlat the top of each checkout page in a Tailwind 4 project. - On every page, set
data-statefor eachli(done, current or todo) andaria-current="step"on one. - Link finished steps back to their pages if you allow editing.
- Add or remove
liitems; the layout divides space evenly.
Licence: DEV3 Snippet Licence. Free to use, change and ship in personal and commercial projects. Attribution is appreciated, not required. Please do not redistribute snippets as a collection.