Skip to content

Horizontal Stepper

Compact horizontal progress stepper for CI bars, wizard headers, and breadcrumb-style progress. It is display-only: the active value derives completed, active, and pending step states.

Preview

Installation

$pnpm exec dgadd add ui/horizontal-stepper
[Installs to]src/components/ui/horizontal-stepper[Item]ui/horizontal-stepper

dgadd is not public on npm yet. Until the first release, pack @diffgazer/add from the repository and install that tarball into this app, which is what puts dgadd on pnpm exec.

UI components require Tailwind CSS v4. Local copy mode imports src/styles/styles.css; package mode uses @diffgazer/ui CSS once packages are available.

Usage

tsx
import { HorizontalStepper } from "@/components/ui/horizontal-stepper";export default function HorizontalStepperDefault() {  return (    <HorizontalStepper value="review">      <HorizontalStepper.Step value="select">SELECT</HorizontalStepper.Step>      <HorizontalStepper.Step value="review">REVIEW</HorizontalStepper.Step>      <HorizontalStepper.Step value="confirm">CONFIRM</HorizontalStepper.Step>      <HorizontalStepper.Step value="done">DONE</HorizontalStepper.Step>    </HorizontalStepper>  );}

Examples

Variants

Preview

Variant: ASCII (start, mid-run, done)

Preview

Compact / Constrained Width

Preview

Progress

Preview

API Reference

HorizontalStepper

NameTypeDefaultDescription
valuerequiredstringId of the active step.
variant"ascii" | "numbered" | "breadcrumb""ascii"Visual variant. Drives glyphs, connectors, and label typography.
compactbooleanfalseForces the compact treatment (connectors hidden, only the active step labelled and prefixed with "Step 3/6 ·", glyph run windowed to previous/active/next with elision counters). When false the stepper adopts each tier automatically: the compact treatment below a 36rem container, and the window at a threshold derived from the variant and the step count. Below a 14rem container only the active step remains, with or without this prop.
aria-labelstring"Progress"Accessible name for the root ordered list.
childrenrequiredReactNodeHorizontalStepper.Step children in render order. Their value props define the step ids.
classNamestringAdditional class names merged onto the root ordered list.

HorizontalStepper.Step

NameTypeDefaultDescription
valuerequiredstringStep id matched against the parent value to derive status and order.
childrenrequiredReactNodeStep label.
classNamestringAdditional class names merged onto the step item.

Data attributes

AttributeApplies toValuesDescription
data-variantHorizontalStepper"ascii" | "numbered" | "breadcrumb"Visual variant on the root ordered list.
data-statusHorizontalStepper.Step"completed" | "active" | "pending"Derived step status. It is also the only hook connector styling needs: the numbered variant fills the incoming segment on data-status=completed.
data-counterHorizontalStepper.Steppresent in numbered variantCSS-counter hook for pending and active numbered indicators.

Accessibility

Keyboard Navigation

HorizontalStepper is display-only. It renders list semantics and aria-current for the active step, but it does not own keyboard handlers.

Notes

Status Derivation

Rendered HorizontalStepper.Step children define order. Steps before value are completed, the matching step is active, and following steps are pending.

Variants

ascii renders inline bracket glyphs with text connectors, numbered renders a numbered indicator on a continuous line, and breadcrumb renders slash-separated labels. Completed indicators and connectors read the primary token, so progress stays monochrome in both themes; the status palette is reserved for meaning.

Glyph Hierarchy

ascii glyphs share the form family's grammar: the brackets render muted as chrome and the inner mark ([x], [~]) keeps the step's status tone at bold weight, so completed and active marks carry the contrast and a pending [ ] reads entirely as chrome. The visible text is unchanged.

Root Element

The root accepts the full ordered-list contract: id, data-*, ref, and any other <ol> attribute is spread onto the element, matching the vertical Stepper.

Constrained Containers

Steps and connectors never break internally, so a narrow parent cannot wrap a label mid-word or split the [ ] glyph across two lines. The root declares a container query instead, in three tiers. Below 36rem of inline space the stepper collapses to the compact treatment: connectors drop out, non-active labels leave the layout, and the active label is prefixed with "Step 3/6 ·". Narrower still, the glyph run becomes a viewfinder window — previous, active, next, plus muted "+2" / "+1" counters for the steps it elides — which makes the run a constant width for any step count. That window engages from need, not from a blanket width: the threshold is keyed by variant and step count, so a four-step ascii run keeps its full run down to 18rem while a twelve-step run windows from 32rem. Below 14rem only the active step remains, but its glyph never drops: the stepper always shows progress. Because every switch is a container query and not a viewport breakpoint, the same stepper adapts inside a sidebar, a dialog, and a full-width page without the consumer branching. Pass compact to force the first two tiers at any width; the narrowest still follows the container.

Accessibility

The root is an ordered list named by aria-label (default "Progress"). The active item exposes aria-current="step", and each item includes screen-reader status text: Completed, Current, or Upcoming. Every compact tier only collapses things visually — every step and its label stay in the accessibility tree at every width. The "Step 3/6 ·" prefix and the "+2" / "+1" elision counters are aria-hidden, because list position and the step statuses already carry that information.

Source

Install via CLI: pnpm exec dgadd add ui/horizontal-stepper.

Highlighted source loads after this disclosure opens. Browse the source repository.