VIII Wayfinding

Star on GitHubFeedback
VIII WayfindingNative HTMLot-steps

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 Steps

Example

margin
  1. A paragraph on what you're making and who it's for.

  2. Agree the scope

    We send back what we'll make, and what we won't.

  3. Book a first call

    Forty minutes, cameras optional.

rise
  1. A paragraph on what you're making and who it's for.

  2. Agree the scope

    We send back what we'll make, and what we won't.

  3. Book a first call

    Forty minutes, cameras optional.

cascade
  1. A paragraph on what you're making and who it's for.

  2. Agree the scope

    We send back what we'll make, and what we won't.

  3. Book a first call

    Forty minutes, cameras optional.

folio
  1. Share the brief

    A paragraph on what you're making and who it's for.

  2. Agree the scope

    We send back what we'll make, and what we won't.

  3. Book a first call

    Forty minutes, cameras optional.

margin, second
  1. Brief

  2. Scope

  3. Call

rise, first
  1. Brief

  2. Scope

  3. Call

rise, last
  1. Brief

  2. Scope

  3. Call

cascade, first
  1. Brief

  2. Scope

  3. Call

cascade, last
  1. Brief

  2. Scope

  3. Call

folio, second
  1. Brief

  2. Scope

  3. Call

folio, all done
  1. Brief

  2. Scope

  3. 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.json

Usage

This example also uses Button.

npx shadcn@latest add https://thedirectors.agency/ui/r/button.json
steps-example.tsx
"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 an h3.ot-step-title and its text. data-current marks where the person is and sets aria-current="step"; data-done marks what's behind them.
  • margin (the default): the numbers are a real sequence, so they are set large and thin at --ot-f in 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-fraction at --ot-ff (--ot-f below 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 and aria-current. Moving on, the figure turns over figure by figure (the fraction's roll). All done, the figure goes to ink.
  • value / defaultValue / onValueChange on 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 own current and done win when given. With onValueChange, 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-rows or plain prose.

Props

Props of Steps
PropTypeDefaultWhat 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 / onValueChangenumber, (value) => voidOn 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 currentbooleanThe step the person is on. Its numeral is the view's one accent. Left out, Steps' value decides.
Step donebooleanA finished step: its numeral turns ink. Left out, Steps' value decides.

Craft

Where its move comes from

Where its move comes from
ComponentReferenceMove
Steps"Less is more." 01Large thin figures in the margin
Steps, riseSPECTRA; Paul Rand's gridNumerals 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, folioSPECTRA's 1/3; "Less is more." 01The whole sequence folded into one fraction beside the step you're on

How it moves

How it moves
ComponentArticulationWhat moves
StepsSpiccatoThe current numeral lands
Steps, riseRiseThe reached numeral rises out of the line
Steps, cascadeOnwardThe arrow steps forward into place
Steps, folioRollThe figure turns over to the new step, figure by figure