Stepper
Multi-step progress indicator with horizontal/vertical layout, clickable navigation, and per-step state overrides.
Playground
- Account Create your account
- 2Profile Set up your profile
- 3Review Review and submit
<Stepper>
<StepperStep label="Account" description="Create your account" />
<StepperStep label="Profile" description="Set up your profile" />
<StepperStep label="Review" description="Review and submit" />
</Stepper>01 Examples
Checkout wizard
- 1Account Create account
- 2Profile Your details
- 3Preferences Customize Optional
- 4Done All set
<div class="flex w-full flex-col gap-4">
<div class="flex items-center gap-2">
<span class="text-text-tertiary text-xs font-medium">Active step:</span>
<Badge size="xs" intent="primary" variant="soft">{interactiveStep + 1} / 4</Badge>
</div>
<Stepper bind:activeStep={interactiveStep} clickable>
<StepperStep label="Account" description="Create account" />
<StepperStep label="Profile" description="Your details" />
<StepperStep label="Preferences" description="Customize" optional />
<StepperStep label="Done" description="All set" />
</Stepper>
<div class="flex gap-2">
<Button
size="sm"
variant="outlined"
disabled={interactiveStep === 0}
onclick={() => (interactiveStep = Math.max(0, interactiveStep - 1))}
>
Back
</Button>
<Button
size="sm"
disabled={interactiveStep === 3}
onclick={() => (interactiveStep = Math.min(3, interactiveStep + 1))}
>
Next
</Button>
</div>
</div>Mixed per-step states
- Account Completed
- Payment Card declined
- Shipping Verify address
- 4Review Final check
<Stepper activeStep={2}>
<StepperStep label="Account" description="Completed" />
<StepperStep label="Payment" description="Card declined" state="error" />
<StepperStep label="Shipping" description="Verify address" state="warning" />
<StepperStep label="Review" description="Final check" />
</Stepper>Optional steps in onboarding
- Account Required
- 2Avatar Upload photo Optional
- 3Bio Tell us about you Optional
- 4Finish Complete setup
<Stepper activeStep={1}>
<StepperStep label="Account" description="Required" />
<StepperStep label="Avatar" description="Upload photo" optional />
<StepperStep label="Bio" description="Tell us about you" optional />
<StepperStep label="Finish" description="Complete setup" />
</Stepper>02 Vertical Stepper
Vertical with content per step
- Create Account Enter your email and password
Enter your credentials to create a new account. We'll send a verification email to confirm your address.
- 2Personal Info Tell us about yourself
Fill in your name, date of birth, and contact information. This helps us personalize your experience.
- 3Preferences Customize your experience Optional
Choose your notification preferences, language, and theme settings.
- 4Complete Review and finish
<Stepper activeStep={1} orientation="vertical">
<StepperStep label="Create Account" description="Enter your email and password">
<div class="bg-surface-elevated border-border-subtle rounded-contain border p-4">
<p class="text-text-secondary text-sm">
Enter your credentials to create a new account. We'll send a verification email to
confirm your address.
</p>
</div>
</StepperStep>
<StepperStep label="Personal Info" description="Tell us about yourself">
<div class="bg-surface-elevated border-border-subtle rounded-contain border p-4">
<p class="text-text-secondary text-sm">
Fill in your name, date of birth, and contact information. This helps us personalize
your experience.
</p>
</div>
</StepperStep>
<StepperStep label="Preferences" description="Customize your experience" optional>
<div class="bg-surface-elevated border-border-subtle rounded-contain border p-4">
<p class="text-text-secondary text-sm">
Choose your notification preferences, language, and theme settings.
</p>
</div>
</StepperStep>
<StepperStep label="Complete" description="Review and finish" />
</Stepper>Vertical with error state
- Account Created Email verified
- Payment Failed Card was declined
Your card ending in •••• 4242 was declined. Please update your payment method.
- 3Ship Order Awaiting payment
<Stepper activeStep={2} orientation="vertical">
<StepperStep label="Account Created" description="Email verified" />
<StepperStep label="Payment Failed" description="Card was declined" state="error">
<div class="border-danger/20 bg-danger/5 rounded-contain border p-4">
<p class="text-danger text-sm font-medium">
Your card ending in •••• 4242 was declined. Please update your payment method.
</p>
</div>
</StepperStep>
<StepperStep label="Ship Order" description="Awaiting payment" />
</Stepper>03 Customization
slotClasses Override
- Draft Write content
- 2Review Get feedback
- 3Publish Go live
<Stepper
activeStep={1}
slotClasses={{
separator: 'h-1 rounded-none'
}}
>
<StepperStep label="Draft" description="Write content" />
<StepperStep label="Review" description="Get feedback" />
<StepperStep label="Publish" description="Go live" />
</Stepper>Dark Glassmorphism
- Design
- 2Develop
- 3Deploy
<Stepper
unstyled
activeStep={1}
class="flex w-full items-center [&>li:last-child_[data-stepper-separator]]:hidden"
>
<StepperStep
unstyled
label="Design"
slotClasses={{
stepItem: 'flex items-center [&:not(:last-child)]:flex-1',
step: 'flex items-center gap-2.5 shrink-0',
indicator:
'flex size-9 items-center justify-center rounded-full text-sm font-semibold bg-white/25 text-white backdrop-blur-md border-2 border-white/30',
label: 'text-sm font-medium text-white/90',
separator: 'h-0.5 flex-1 mx-3 bg-white/20 rounded-full'
}}
/>
<StepperStep
unstyled
label="Develop"
slotClasses={{
stepItem: 'flex items-center [&:not(:last-child)]:flex-1',
step: 'flex items-center gap-2.5 shrink-0',
indicator:
'flex size-9 items-center justify-center rounded-full text-sm font-semibold bg-white/40 text-white backdrop-blur-md border-2 border-white/50 shadow-lg shadow-white/10',
label: 'text-sm font-semibold text-white',
separator: 'h-0.5 flex-1 mx-3 bg-white/20 rounded-full'
}}
/>
<StepperStep
unstyled
label="Deploy"
slotClasses={{
stepItem: 'flex items-center',
step: 'flex items-center gap-2.5 shrink-0',
indicator:
'flex size-9 items-center justify-center rounded-full text-sm font-semibold bg-white/10 text-white/50 backdrop-blur-md border-2 border-white/15',
label: 'text-sm font-medium text-white/50',
separator: 'hidden'
}}
/>
</Stepper>Terminal Progress
- BUILD
- TEST
- 3DEPLOY
- 4MONITOR
<Stepper
unstyled
activeStep={2}
class="flex w-full items-center font-mono [&>li:last-child_[data-stepper-separator]]:hidden"
>
<StepperStep
unstyled
label="BUILD"
slotClasses={{
stepItem: 'flex items-center [&:not(:last-child)]:flex-1',
step: 'flex items-center gap-2 shrink-0',
indicator:
'flex size-7 items-center justify-center rounded text-xs font-bold bg-emerald-500/20 text-emerald-400 border border-emerald-500/30',
label: 'text-xs text-emerald-300 tracking-wider',
separator: 'h-px flex-1 mx-3 bg-emerald-500/20'
}}
/>
<StepperStep
unstyled
label="TEST"
slotClasses={{
stepItem: 'flex items-center [&:not(:last-child)]:flex-1',
step: 'flex items-center gap-2 shrink-0',
indicator:
'flex size-7 items-center justify-center rounded text-xs font-bold bg-emerald-500/20 text-emerald-400 border border-emerald-500/30',
label: 'text-xs text-emerald-300 tracking-wider',
separator: 'h-px flex-1 mx-3 bg-emerald-500/20'
}}
/>
<StepperStep
unstyled
label="DEPLOY"
slotClasses={{
stepItem: 'flex items-center [&:not(:last-child)]:flex-1',
step: 'flex items-center gap-2 shrink-0',
indicator:
'flex size-7 items-center justify-center rounded text-xs font-bold bg-emerald-500 text-neutral-950 border border-emerald-400 shadow-[0_0_12px_rgba(52,211,153,0.3)]',
label: 'text-xs text-emerald-300 tracking-wider font-bold',
separator: 'h-px flex-1 mx-3 bg-emerald-500/20'
}}
/>
<StepperStep
unstyled
label="MONITOR"
slotClasses={{
stepItem: 'flex items-center',
step: 'flex items-center gap-2 shrink-0',
indicator:
'flex size-7 items-center justify-center rounded text-xs font-bold bg-transparent text-neutral-400 border border-neutral-600',
label: 'text-xs text-neutral-400 tracking-wider',
separator: 'hidden'
}}
/>
</Stepper>Wizard chrome like the glass or terminal looks above belongs in BlocksProvider presets — presets.Stepper for the shell, presets.StepperStep for the per-step slots — applied via preset. See Customization.
04 Accessibility
Built-in ARIA
The stepper renders as an <ol> with aria-label="Progress". The active step is marked with aria-current="step". Clickable steps get role="button" for screen reader identification. The data-orientation attribute exposes the layout direction.
Keyboard Navigation
Tab moves focus between clickable steps. Enter / Space activates the focused step. Focus rings use focus-visible: for keyboard-only visibility.
Step States
Completed, error, and warning steps use distinct icons (checkmark, X, warning triangle) in
addition to color, ensuring status is never conveyed by color alone. Disabled steps are
removed from the tab order via pointer-events-none and visual opacity-50.
Reduced Motion
All transitions use design token durations (--blocks-duration-fast, --blocks-duration-normal) which are reduced
automatically when prefers-reduced-motion is active.
05 API Reference
Prop | Type | Default | Description | |
|---|---|---|---|---|
children required | Snippet | — | StepperStep children. | |
activeStep | number | 0 | Current active step index (0-based). Supports bind:activeStep. | |
class | string | — | Extra classes merged onto the root element. | |
clickable | boolean | false | Allow clicking step indicators to navigate between steps. | |
disabled | boolean | false | Disable all steps and prevent navigation. | |
linear | boolean | false | Restrict navigation to sequential order — only completed steps and the next step are clickable. | |
onStepChange | (step: number) => void | — | Fires when a step is activated via click. Passes the new step index. | |
orientation | horizontalvertical | 'horizontal' | Orientation property for the Stepper component | |
preset | string | — | Apply a named preset registered via <BlocksProvider presets={{ Stepper: {...} }}>.
Prefer this over class overrides when the requested look falls outside the
semantic intent palette — presets keep hover/active/dark-mode logic coherent
and make the custom look reusable across the project. | |
responsive | boolean | { breakpoint?: number } | false | Container-responsive mode: when the Stepper's container is narrower than
breakpoint, automatically switch to orientation="vertical" and
variant="minimal" so the steps stay readable.
- false (default): never auto-switch.
- true: switch below 640 px container width.
- { breakpoint: 480 }: switch below 480 px.
Uses ResizeObserver on the root element — works inside Drawers, Cards,
or split layouts where viewport-based media queries miss the actual
available width. | |
size | smmdlg | 'md' | Size variant that controls dimensions and spacing of the Stepper | |
slotClasses | Partial<Record<StepperSlots, string>> | — | Per-slot class overrides. Slots: base | stepItem | step | indicatorColumn | indicator | labelGroup | label | description | separator | content | |
tier | InteractiveTier | 'commit' | Semantic radius tier propagated to every StepperStep. Default commit
— step indicators read as identity circles. Set to modify (or
inherit via TierContext from a wrapping <Toolbar tier="modify">) to
render a compact soft-rectangle stepper for inline wizards. | |
unstyled | boolean | — | Remove all default tv() classes. | |
variant | defaultoutlinedminimal | 'default' | Visual style of step indicators. | |
...HTMLAttributes<HTMLOListElement> inherited | HTMLAttributes | — | HTML attributes (excluding: 'children') | |
...StepperVariants variant | VariantProps | — | Styling variants from StepperVariants |
06 Types
Local type definitions used by this component.
Name | Kind | Category | Used by | Description | |
|---|---|---|---|---|---|
StepperContext | interface | helper | 0 | Context for Stepper ↔ StepperStep communication | |
StepperProps | interface | props | 0 | — | |
StepperStepProps | interface | props | 0 | Single step within a Stepper. Renders an indicator, label, and optional content (vertical only). | |
StepperVariants | type | variant | 0 | — | |
StepperSlots | type | variant | 0 | Slot names derived from the tv() config — single source of truth for slotClasses. | |
InteractiveTier | type | helper | 1 | Semantic radius tier for interactive surfaces (3-tier system).
- commit → r-human (CTA, identity, status declarations)
- modify → r-interactive (fields, navigation, secondary actions)
Container components (Card, Alert, Toolbar surface, …) live in a third
tier contain (r-structure) which is **not** part of this propagation
context — those surfaces are always r-structure by design and have no
tier-flip use case. |
07 Installation
Import
import { Stepper, StepperStep } from '@urbicon-ui/blocks';