Step Progress
A thin horizontal row of indicators representing steps in a flow. The current step is visually emphasised (larger / wider / filled); past steps fade to a muted accent tint; future steps stay neutral. Three geometries — dots, pills, numbered — pick the one that matches the density of your context.
Different from StepTimeline: this primitive carries no labels and no connectors. Use it for wizards, multi-step forms, slideshow paginators, and any other context where step count + cursor are the only signals worth conveying.
Customize
Total
5
Current
2
Variant
dots
Size
md
Installation
npx shadcn@latest add https://craftbits.dev/r/step-progress.jsonUsage
import { StepProgress } from "@craft-bits/core";
<StepProgress aria-label="Sign-up progress" total={5} current={2} />Pills (the current-step capsule widens — a "now playing" cursor):
<StepProgress total={4} current={0} variant="pills" />Numbered, for explicit step counts (≤ 9 ideally — beyond that, prefer StepTimeline):
<StepProgress total={4} current={1} variant="numbered" />Understanding the component
- Three indicator geometries.
dots(small filled circles),pills(short capsules that widen on the current step),numbered(circles hosting the 1-based step label). All three share the same[done | current | upcoming]palette logic — only the geometry changes. - Snap on the cursor. The current-step emphasis (scale-up for dots/numbered, width grow for pills) rides on
SPRINGS.snapso the cursor lands crisply rather than floats. - Reduced motion. When
prefers-reduced-motion: reduceis set, the cursor jumps to its new position with no spring — indicators repaint instantly. - Clamped indices.
totalis floored to≥ 1;currentis clamped into[0, total - 1]. Out-of-range inputs render the closest valid cursor — the component never throws. - Semantic markup. Renders as a
<div role="progressbar">witharia-valuemin={1},aria-valuemax={total},aria-valuenow={current + 1}, and anaria-valuetextof"Step X of Y". Indicators arearia-hidden.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
total | number | required | Total number of steps. Floored to ≥ 1. |
current | number | required | 0-indexed current step. Clamped into [0, total - 1]. |
variant | 'dots' | 'pills' | 'numbered' | 'dots' | Indicator geometry. |
size | 'sm' | 'md' | 'lg' | 'md' | Indicator dimensions. |
className | string | — | Merged onto the root <div> via cn(). |
Accessibility
- Renders with
role="progressbar"plusaria-valuemin={1},aria-valuemax={total},aria-valuenow={current + 1}, andaria-valuetext={"Step X of Y"}so assistive tech announces both the numeric and natural-language reading. - Indicator elements are marked
aria-hidden="true"— screen readers read the bar's value, not the dot count. - The component is not auto-labelled. Provide
aria-labeloraria-labelledbyon the root for context (e.g.,"Sign-up progress"). prefers-reduced-motion: reduceshort-circuits the cursor spring — indicators snap to their new state with no animation.
Credits
- Extracted from:
algoflashcards(src/lessons/primitives/chrome/StepProgress.tsx). Generalized: the source rendered a thin progress bar plus an"N / M"label via the project'sStepChromecompound; the library version trades that for the canonical "row of indicators" idiom used by wizards and paginators, splits it into three geometric variants, and replaces the project'strackInfo.hexcolor prop with the--cb-accenttoken. Use the siblingStepTimelinewhen you want labels + connectors.