Stepper
Show progress through a sequence, including completed, current, upcoming and failed steps.
import { Stepper, StepperDetour, StepperStep } from '@youj/react'
Installation & usage
npm i @youj/reactFor an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.
Use a stepper to show progress through a fixed sequence people move through in order: checkout, onboarding, a setup flow. It shows state, it doesn't hold the steps' content. For a flow that shows one step at a time with Back and Continue built in, use a Wizard, which draws its own stepper. For a flow that spans pages, with steps as links, use Stepper Nav. For progress that has no named steps, use a Progress bar.
import { Stepper, type StepperStep } from '@youj/react'
const steps: StepperStep[] = [
{ id: 'cart', title: 'Cart' },
{ id: 'shipping', title: 'Shipping' },
{ id: 'payment', title: 'Payment' },
{ id: 'review', title: 'Review' },
]
export function CheckoutProgress({ current }: { current: number }) {
return <Stepper steps={steps} current={current} aria-label="Checkout" />
}current is the index of the step in progress. Steps before it are complete, and current={steps.length} marks every step done. The current step's disc is the solid one, in the accent; done steps step back to a tint with their check, and upcoming ones are outlined. Pass onStepClick and done steps, and the current one, become buttons that go back to them. Upcoming steps stay inert, so the stepper can't skip ahead of the flow's own validation.
Description
A four-step checkout stepper with Back and Continue buttons; done steps can be pressed to go back.
A vertical stepper with descriptions.
orientation="vertical" stacks the steps with their descriptions, for a sidebar beside the flow. The connector fills downwards.
A step's description sits under its title, and optional adds "Optional" to it.
Steps showing an error and every step complete.
A step with error shows a warning mark and a danger title, whatever its position, so a failed validation stays visible after the person moves on. The last row shows every step complete.
On narrow screens, compact hides the titles and descriptions below the sm breakpoint and leaves the discs and connectors. The titles are still read out, so the steps keep their names.
A horizontal stepper with a detour drawn over the steps it bypasses.
detour draws a rerouted stretch on a horizontal stepper: a dotted arc in the accent leaves the step at from, passes over the steps it bypasses and lands on the step at to, with its label and detail riding the top. Bypassed steps carry data-bypassed. It's measured from the discs, so it follows resizes and RTL; a vertical stepper ignores it.
A launch plan as dated milestones, with the submission deadline flagged.
flagged calls a step out without failing it: until it's done, its disc wears a flag and its description takes the danger ink, and the disc carries data-yj-flagged. The flag is decorative, so put the reason in the description ("Oct 30, deadline"), which is read with the title. Use invalid instead when the step has actually gone wrong.
Code
stepper-demo.tsx, the Demo story. The component itself comes from the package.
Reference
Propssteps, current, orientation… (12)
Props
| Prop | Type | |
|---|---|---|
stepsRequired | readonly StepperStep[] | – |
currentRequired | numberIndex of the current step. Steps before it are complete; | – |
orientation | 'horizontal' | 'vertical'default 'horizontal' | 'horizontal' |
onStepClick | (index: number) => voidMakes done steps (and the current one) buttons that go back to them. | – |
stepHref | (index: number) => string | undefinedMakes done steps (and the current one) links to their pages instead, for
flows that span pages. | – |
compact | booleanHides titles and descriptions below the default false | false |
detour | StepperDetourA rerouted stretch, drawn as an arc from one step over the ones it bypasses to the step it rejoins. Horizontal only. | – |
Other props pass through: extends ComponentProps<'ol'>. | ||
Steps
Each entry in steps is a StepperStep:
| Prop | Type | |
|---|---|---|
idRequired | stringA stable key. | – |
titleRequired | ReactNodeThe step's name. | – |
description | ReactNodeA second line under the title. | – |
optional | booleanAdds "Optional" under the title. | – |
invalid | booleanMarks the step as failed (e.g. its validation did not pass). | – |
flagged | booleanCalls a step out without failing it (a deadline, a milestone to watch): until it's done its disc wears a flag and its description the danger ink. The flag is decorative, so say why in the description. | – |
MotionConnector fill, disc, title color… · reduced motion
Motion
| Moment | Tokens | ||
|---|---|---|---|
| Connector fillscales from the inline start (or down) when the step before it is done, and drains back if you return | duration.slowease.standard340ms · cubic-bezier(0.2, 0, 0, 1) | 340mscubic-bezier(0.2, 0, 0, 1) | |
| Discbackground, border and ring change as the step changes state | duration.baseease.standard220ms · cubic-bezier(0.2, 0, 0, 1) | 220mscubic-bezier(0.2, 0, 0, 1) | |
| Title color | duration.baseease.standard220ms · cubic-bezier(0.2, 0, 0, 1) | 220mscubic-bezier(0.2, 0, 0, 1) | |
| Check and warning marksYouj icons draw on when they mount | no token | ||
| Detourthe arc draws from the step it leaves, and its label rises in as the line arrives | duration.deliberateease.emphasized560ms · cubic-bezier(0.6, 0, 0.1, 1) | 560mscubic-bezier(0.6, 0, 0.1, 1) |
Reduced motion
Connectors fill and drain at once, and marks appear without drawing.
The fill is a transform on a Motion value, so going back mid-fill drains from where the fill is instead of restarting.
Reduced motion
Connectors fill and drain instantly, discs change color at once, the check and warning marks appear without drawing and a detour is drawn in place. The state reads exactly the same.
AccessibilityARIA, focus, announcements
Accessibility
- The stepper is an ordered list named "Progress" by default. Name it after the flow with
aria-label. - The current step has
aria-current="step", and every step's state is written out after its title for screen readers: "Payment, has an error". - With
onStepClick, the done steps and the current one are real buttons, and withstepHrefthey're links. Upcoming steps aren't focusable. - The discs, connectors and marks are decorative (
aria-hidden); the text carries the state. - A detour's label is read after the step it leaves, with where it rejoins ("Detour, rejoins at step 4",
stepDetourin the labels).