Steps
A real sequence. Numbered in large thin figures in the margin; or the figures stand on one line and rise out of it as you reach them; or the titles step across the page like a broken headline; or, compact, the whole sequence folded into one fraction beside the step you're on.
Report an issue with StepsExample
A paragraph on what you're making and who it's for.
Agree the scope
We send back what we'll make, and what we won't.
Book a first call
Forty minutes, cameras optional.
A paragraph on what you're making and who it's for.
Agree the scope
We send back what we'll make, and what we won't.
Book a first call
Forty minutes, cameras optional.
A paragraph on what you're making and who it's for.
Agree the scope
We send back what we'll make, and what we won't.
Book a first call
Forty minutes, cameras optional.
Share the brief
A paragraph on what you're making and who it's for.
Agree the scope
We send back what we'll make, and what we won't.
Book a first call
Forty minutes, cameras optional.
Brief
Scope
Call
Brief
Scope
Call
Brief
Scope
Call
Brief
Scope
Call
Brief
Scope
Call
Brief
Scope
Call
Brief
Scope
Call
Install
Run it in a project that has the shadcn CLI set up. It brings the 0nlyType base and fraction along.
npx shadcn@latest add https:// thedirectors.agency/ ui/ r/steps.jsonUsage
This example also uses Button.
npx shadcn@latest add https:// thedirectors.agency/ ui/ r/ button.json"use client"import * as React from "react"import { Button } from "@/components/ui/button"import { Step, StepTitle, Steps, type StepsProps } from "@/components/ui/steps"const STEPS = [["Share the brief", "A paragraph on what you're making and who it's for."],["Agree the scope", "We send back what we'll make, and what we won't."],["Book a first call", "Forty minutes, cameras optional."],]function Sequence({ variant, at, go }: { variant: StepsProps["variant"]; at: number; go: (n: number) => void }) {return (<Steps variant={variant} value={at} onValueChange={go}>{STEPS.map(([title, text]) => (<Step key={title}><StepTitle>{title}</StepTitle><p>{text}</p></Step>))}</Steps>)}export default function Example() {const [at, setAt] = React.useState(2)return (<div className="grid gap-y-10"><div className="flex gap-x-6"><Button variant="quiet" disabled={at === 1} onClick={() => setAt(at - 1)}>Back a step</Button><Button variant="quiet" disabled={at === STEPS.length} onClick={() => setAt(at + 1)}>Next step</Button></div>{(["margin", "rise", "cascade", "folio"] as const).map((v) => (<div key={v} className="grid gap-y-4"><span className="ot-label">{v}</span><Sequence variant={v} at={at} go={setAt} /></div>))}</div>)}Contract
- Underneath: native
<ol>. - Anatomy:
<ol class="ot-steps" data-variant="margin | rise | cascade">of<li class="ot-step">, each with anh3.ot-step-titleand its text.data-currentmarks where the person is and setsaria-current="step";data-donemarks what's behind them. - margin (the default): the numbers are a real sequence, so they are set large and thin at
--ot-fin the margin, in two figures as the posters number ("Less is more." 01), in pencil. Done steps' numbers go to ink; the current number takes the accent and lands with spiccato. A hairline stands between steps. - rise: the numerals stand huge and thin (
--ot-ff, weight 100) on one rule-strong hairline running across the page, a column to each step, like letters cut by a line in SPECTRA and Paul Rand's grid. A step to come is sunk into the line, only the top of its figures showing; reached, its numeral rises out whole in the accent (andante, exhale), and it stays standing in ink once done. Below 40rem the steps stack, each on its own line, at--ot-f. - cascade: no numerals. The titles (
mf, 300) are set as one broken headline that steps across the page a space-8 at a time (space-5 below 40rem; the stair stops at the fifth step), the way "Healthy habits → for creatives" breaks its line, so the order is read in the space itself. Behind you in pencil, ahead in graphite; where you are in ink, with the accent arrow after the title pointing on to the next (it steps forward into place with spiccato when the sequence moves on; ← right to left). The last step, reached, closes the headline with an accent full stop instead. - folio: the compact one, after SPECTRA's large 1/3 beside its small notes and "Less is more."'s 01. The sequence folds into one
ot-fractionat--ot-ff(--ot-fbelow 40rem) in.ot-steps-folio: where you are in the accent italic ("02"), of how many small and upright after the pen stroke ("/05"), in two figures. Only the step you're on is shown beside it (data-shown), its title on the figure's baseline and its text under it in graphite; the other steps stay in the<ol>, visually hidden, so a screen reader hears the whole order andaria-current. Moving on, the figure turns over figure by figure (the fraction's roll). All done, the figure goes to ink. value/defaultValue/onValueChangeon Steps: the step you're on, counted from 1 as the numerals are; the steps before it are done, and one past the last means all done. A Step's owncurrentanddonewin when given. WithonValueChange, a done step's title is a<button class="ot-step-back">back to it (not in folio, whose done steps are hidden); pointing or focus draws a hairline under it.- Only for real sequences. A list that isn't an order is
ot-rowsor plain prose.
Props
| Prop | Type | Default | What it does |
|---|---|---|---|
variant | "margin" | "rise" | "cascade" | "folio" | "margin" | On Steps. margin: two-figure numerals, large and thin, in the margin; done ink, current the accent. rise: the numerals stand huge on one hairline across the page; a step to come is sunk into the line with only its top showing, and when it is reached its numeral rises out whole in the accent. Below 40rem the steps stack. cascade: no numerals; the titles step across the page like a broken headline, behind you in pencil and ahead in graphite, and where you are in ink with an accent arrow pointing on (a full stop on the last step). folio is the compact one: the sequence folds into one fraction ("02/05", where you are in the accent italic), and only the step you're on is shown beside it, its title on the figure's baseline; the others stay in the list for screen readers. Moving on, the figure turns over. |
value / defaultValue / onValueChange | number, (value) => void | On Steps. The step you're on, counted from 1 as the numerals are, controlled or not; the steps before it are done, and one past the last means all done. Left out, each Step's current and done say where you are. With onValueChange, the titles of the steps behind you become buttons back to them (not in folio, which hides them; give it your own Back button). | |
Step current | boolean | The step the person is on. Its numeral is the view's one accent. Left out, Steps' value decides. | |
Step done | boolean | A finished step: its numeral turns ink. Left out, Steps' value decides. |
Craft
Where its move comes from
| Component | Reference | Move |
|---|---|---|
| Steps | "Less is more." 01 | Large thin figures in the margin |
| Steps, rise | SPECTRA; Paul Rand's grid | Numerals sunk into a line rise out as you reach them |
| Steps, cascade | "Healthy habits → for creatives" | The titles step across the page; an arrow points on |
| Steps, folio | SPECTRA's 1/3; "Less is more." 01 | The whole sequence folded into one fraction beside the step you're on |
How it moves
| Component | Articulation | What moves |
|---|---|---|
| Steps | Spiccato | The current numeral lands |
| Steps, rise | Rise | The reached numeral rises out of the line |
| Steps, cascade | Onward | The arrow steps forward into place |
| Steps, folio | Roll | The figure turns over to the new step, figure by figure |