{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "steps",
  "title": "Steps",
  "description": "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.",
  "registryDependencies": [
    "https://thedirectors.agency/ui/r/0nlytype.json",
    "https://thedirectors.agency/ui/r/fraction.json"
  ],
  "files": [
    {
      "path": "registry/0nlytype/ui/steps.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Fraction } from \"@/components/ui/fraction\"\n\ntype StepsProps = Omit<React.ComponentProps<\"ol\">, \"defaultValue\"> & {\n  /**\n   * margin: large thin numerals in the margin.\n   * rise: the numerals stand on one line across the page; those to come are sunk into it and rise out as you reach them.\n   * cascade: no numerals; the titles step across the page like a broken headline, an arrow pointing on from where you are.\n   * folio: the compact one. The sequence folds into one fraction, \"02/05\", beside the step you're on.\n   */\n  variant?: \"margin\" | \"rise\" | \"cascade\" | \"folio\"\n  /**\n   * The step you're on, counted from 1 as the numerals are; the steps before it are done. One past the last means\n   * all done. Left out, each Step's own `current` and `done` say where you are.\n   */\n  value?: number\n  defaultValue?: number\n  /** Makes the steps behind you a way back: their titles become buttons that call this with the step's number. */\n  onValueChange?: (value: number) => void\n}\n\nconst Sequence = React.createContext<{ at?: number; go?: (n: number) => void; shown?: number }>({})\nconst Place = React.createContext(0) // this step's number, from 1\nconst Reached = React.createContext({ n: 0, done: false })\n\n/** A real sequence, numbered. Only use it when the order matters. */\nfunction Steps({ className, variant = \"margin\", value, defaultValue, onValueChange, children, ...props }: StepsProps) {\n  const [own, setOwn] = React.useState(defaultValue)\n  const kids = React.Children.toArray(children).filter(React.isValidElement<StepProps>)\n  const at = value ?? own\n  // The folio says where you are as one figure: the step you're on, or the last one reached.\n  const here = Math.min(Math.max(at ?? (kids.findIndex((k) => k.props.current) + 1 || kids.filter((k) => k.props.done).length + 1), 1), kids.length)\n  const shown = variant === \"folio\" ? here : undefined\n  const go = onValueChange\n    ? (n: number) => {\n        if (value === undefined) setOwn(n)\n        onValueChange(n)\n      }\n    : undefined\n  const list = (\n    <ol data-slot=\"steps\" data-variant={variant} className={cn(\"ot-steps\", className)} {...props}>\n      <Sequence.Provider value={{ at, go, shown }}>\n        {kids.map((kid, i) => (\n          <Place.Provider key={kid.key ?? i} value={i + 1}>\n            {kid}\n          </Place.Provider>\n        ))}\n      </Sequence.Provider>\n    </ol>\n  )\n  if (variant !== \"folio\") return list\n  // The list stays whole for a screen reader; only the step shown beside the figure is seen.\n  return (\n    <div className=\"ot-steps-folio\">\n      <Fraction count={String(here).padStart(2, \"0\")} total={String(kids.length).padStart(2, \"0\")} aria-hidden=\"true\" />\n      {list}\n    </div>\n  )\n}\n\ntype StepProps = React.ComponentProps<\"li\"> & {\n  /** The step the person is on: its numeral is the view's one accent. Left out, Steps' `value` decides. */\n  current?: boolean\n  done?: boolean\n}\n\nfunction Step({ className, current, done, ...props }: StepProps) {\n  const { at, shown } = React.useContext(Sequence)\n  const n = React.useContext(Place)\n  const here = current ?? (at !== undefined && n === at)\n  const behind = done ?? (at !== undefined && n < at)\n  return (\n    <Reached.Provider value={{ n, done: behind }}>\n      <li\n        data-slot=\"step\"\n        data-current={here || undefined}\n        data-done={behind || undefined}\n        data-shown={n === shown || undefined}\n        aria-current={here ? \"step\" : undefined}\n        className={cn(\"ot-step\", className)}\n        {...props}\n      />\n    </Reached.Provider>\n  )\n}\n\n/** The step's name. Behind you, with `onValueChange` on Steps, it's a button back to that step. */\nfunction StepTitle({ className, children, ...props }: React.ComponentProps<\"h3\">) {\n  const { go, shown } = React.useContext(Sequence)\n  const { n, done } = React.useContext(Reached)\n  return (\n    <h3 data-slot=\"step-title\" className={cn(\"ot-step-title\", className)} {...props}>\n      {go && done && shown === undefined ? ( // ponytail: the folio hides the steps behind you, so it offers no way back; pair it with your own Back button\n        <button type=\"button\" className=\"ot-step-back\" onClick={() => go(n)}>\n          {children}\n          <span className=\"ot-sr\">, done. Go back to this step.</span>\n        </button>\n      ) : (\n        children\n      )}\n    </h3>\n  )\n}\n\nexport { Steps, Step, StepTitle, type StepsProps, type StepProps }\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/0nlytype/styles/steps.css",
      "content": "@layer components {\n  /* ── ot-steps ── a real sequence: large thin numerals in pencil, two figures as the posters number\n     (\"Less is more.\" 01), a hairline between steps; the current numeral is the one accent and lands with a\n     small rebound when the sequence moves on. */\n  .ot-steps { list-style: none; margin: 0; padding: 0; counter-reset: ot-step; display: grid; gap: var(--ot-space-6); }\n  .ot-step {\n    counter-increment: ot-step;\n    display: grid; grid-template-columns: max-content minmax(0, 1fr); column-gap: var(--ot-space-5);\n    padding-block-start: var(--ot-space-4); border-block-start: var(--ot-hairline) solid var(--ot-rule);\n  }\n  .ot-step::before {\n    content: counter(ot-step, decimal-leading-zero); grid-row: 1 / span 3; align-self: start;\n    font-size: var(--ot-f); line-height: 0.9; font-weight: 200; font-stretch: 88%; letter-spacing: -0.02em;\n    font-variant-numeric: lining-nums tabular-nums; color: var(--ot-pencil);\n  }\n  .ot-step > * { grid-column: 2; min-inline-size: 0; }\n  .ot-step > * + * { margin-block-start: var(--ot-space-3); } /* not row-gap: the numeral's spanned rows would each add one */\n  .ot-step[data-done]::before { color: var(--ot-ink); }\n  .ot-step[data-current]::before { color: var(--ot-accent); font-weight: 300; animation: ot-land var(--ot-moderato) var(--ot-spiccato); }\n  .ot-step-title { margin: 0; font-size: var(--ot-mp); line-height: var(--ot-mp-lh); letter-spacing: var(--ot-mp-tr); font-weight: 400; color: var(--ot-ink); }\n\n  /* rise: the numerals stand on one hairline across the page, huge and thin, like letters cut by a line in\n     SPECTRA and Paul Rand's grid. A step to come is sunk into the line, only its top showing; reached, its\n     numeral rises out whole (the accent), and it stays standing, in ink, once done. */\n  .ot-steps[data-variant=\"rise\"] { grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 0; }\n  .ot-steps[data-variant=\"rise\"] > .ot-step { grid-template-columns: minmax(0, 1fr); align-content: start; padding: 0; border: 0; }\n  .ot-steps[data-variant=\"rise\"] > .ot-step > * { grid-column: 1; padding-inline-end: var(--ot-space-5); }\n  .ot-steps[data-variant=\"rise\"] > .ot-step::before {\n    grid-row: auto; box-sizing: border-box; block-size: calc(0.74em + var(--ot-hairline)); overflow: clip;\n    padding-block-start: var(--ot-step-sink, 0.46em); margin-block-end: var(--ot-space-4);\n    font-size: var(--ot-ff); line-height: 0.74; font-weight: 100; font-stretch: 100%; letter-spacing: -0.04em;\n    border-block-end: var(--ot-hairline) solid var(--ot-rule-strong); animation: none;\n    transition: padding-block-start var(--ot-andante) var(--ot-exhale), color var(--ot-moderato);\n  }\n  .ot-steps[data-variant=\"rise\"] > .ot-step:is([data-done], [data-current]) { --ot-step-sink: 0em; }\n  .ot-steps[data-variant=\"rise\"] > .ot-step[data-current]::before { font-weight: 200; }\n  @media (max-width: 40rem) {\n    .ot-steps[data-variant=\"rise\"] { grid-auto-flow: row; row-gap: var(--ot-space-6); }\n    .ot-steps[data-variant=\"rise\"] > .ot-step::before { font-size: var(--ot-f); }\n  }\n\n  /* cascade: no numerals. The titles are set as one broken headline that steps across the page, the way\n     \"Healthy habits → for creatives\" breaks its line, so the order is read in the space itself. Behind you in\n     pencil, ahead in graphite; where you are in ink, with the accent arrow pointing on to the next. The last\n     step, reached, closes the headline with an accent full stop instead. */\n  .ot-steps[data-variant=\"cascade\"] { gap: var(--ot-space-5); }\n  .ot-steps[data-variant=\"cascade\"] > .ot-step { display: block; padding: 0; border: 0; margin-inline-start: calc(var(--ot-step-i, 0) * var(--ot-space-8)); }\n  .ot-steps[data-variant=\"cascade\"] > .ot-step:nth-child(2) { --ot-step-i: 1; }\n  .ot-steps[data-variant=\"cascade\"] > .ot-step:nth-child(3) { --ot-step-i: 2; }\n  .ot-steps[data-variant=\"cascade\"] > .ot-step:nth-child(4) { --ot-step-i: 3; }\n  .ot-steps[data-variant=\"cascade\"] > .ot-step:nth-child(n + 5) { --ot-step-i: 4; } /* ponytail: the stair stops at the fifth step so a long list keeps its measure */\n  .ot-steps[data-variant=\"cascade\"] > .ot-step::before { content: none; }\n  .ot-steps[data-variant=\"cascade\"] > .ot-step > * + * { margin-block-start: var(--ot-space-2); }\n  .ot-steps[data-variant=\"cascade\"] > .ot-step > :not(.ot-step-title) { color: var(--ot-pencil); }\n  .ot-steps[data-variant=\"cascade\"] .ot-step-title {\n    font-size: var(--ot-mf); line-height: var(--ot-mf-lh); letter-spacing: var(--ot-mf-tr); font-weight: 300; color: var(--ot-graphite);\n    transition: color var(--ot-moderato);\n  }\n  .ot-steps[data-variant=\"cascade\"] > .ot-step[data-done] .ot-step-title { color: var(--ot-pencil); }\n  .ot-steps[data-variant=\"cascade\"] > .ot-step[data-current] .ot-step-title { color: var(--ot-ink); font-weight: 400; }\n  .ot-steps[data-variant=\"cascade\"] > .ot-step[data-current] > :not(.ot-step-title) { color: var(--ot-graphite); }\n  .ot-steps[data-variant=\"cascade\"] > .ot-step[data-current] .ot-step-title::after {\n    content: \"\\2009\\2192\" / \"\"; display: inline-block; color: var(--ot-accent); font-weight: 300;\n    animation: ot-step-onward var(--ot-moderato) var(--ot-spiccato) backwards;\n  }\n  .ot-steps[data-variant=\"cascade\"] > .ot-step[data-current]:last-child .ot-step-title::after { content: \".\" / \"\"; }\n  .ot-steps[data-variant=\"cascade\"]:is([dir=\"rtl\"], [dir=\"rtl\"] *) > .ot-step[data-current]:not(:last-child) .ot-step-title::after { content: \"\\2009\\2190\" / \"\"; --ot-step-lr: -1; }\n  @keyframes ot-step-onward { from { opacity: 0; translate: calc(-0.4em * var(--ot-step-lr, 1)) 0; } }\n  @media (max-width: 40rem) {\n    .ot-steps[data-variant=\"cascade\"] > .ot-step { margin-inline-start: calc(var(--ot-step-i, 0) * var(--ot-space-5)); }\n  }\n\n  /* folio: the compact one. The whole sequence folds into one fraction, as SPECTRA prints 1/3 large beside its small\n     notes and \"Less is more.\" sets its 01: where you are in the accent italic, of how many small and upright after the\n     pen stroke. The step you're on hangs beside it, its title on the figure's baseline. The other steps stay in the list,\n     unseen, so a screen reader still hears the whole order. Moving on, the figure turns over (the fraction's roll). */\n  .ot-steps-folio { display: grid; grid-template-columns: max-content minmax(0, 1fr); column-gap: var(--ot-space-5); align-items: first baseline; }\n  .ot-steps-folio > .ot-fraction { font-size: var(--ot-ff); }\n  .ot-steps-folio > .ot-fraction > .ot-yours { color: var(--ot-accent); }\n  .ot-steps-folio:not(:has([data-current])) > .ot-fraction > .ot-yours { color: var(--ot-ink); } /* all done: nowhere left to be */\n  .ot-steps[data-variant=\"folio\"] { display: block; }\n  .ot-steps[data-variant=\"folio\"] > .ot-step { display: block; padding: 0; border: 0; }\n  .ot-steps[data-variant=\"folio\"] > .ot-step::before { content: none; }\n  .ot-steps[data-variant=\"folio\"] > .ot-step > :not(.ot-step-title) { color: var(--ot-graphite); }\n  .ot-steps[data-variant=\"folio\"] > .ot-step:not([data-shown]) {\n    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap;\n  }\n  @media (max-width: 40rem) { .ot-steps-folio > .ot-fraction { font-size: var(--ot-f); } }\n\n  /* A way back: with onValueChange, a done step's title is a button. Pointing draws a hairline under it. */\n  .ot-step-back {\n    appearance: none; margin: 0; padding: 0; border: 0; background: none; font: inherit; letter-spacing: inherit; color: inherit; text-align: inherit; cursor: pointer;\n    text-decoration: underline var(--ot-hairline) transparent; text-underline-offset: 0.2em; transition: text-decoration-color var(--ot-allegro);\n  }\n  .ot-step-back:is(:hover, :focus-visible, [data-force~=\"hover\"]) { text-decoration-color: currentColor; }\n\n  @media (forced-colors: active) {\n    .ot-step[data-current]::before, .ot-steps[data-variant=\"cascade\"] > .ot-step[data-current] .ot-step-title::after, .ot-steps-folio > .ot-fraction > .ot-yours { color: Highlight; }\n  }\n}\n",
      "type": "registry:file",
      "target": "styles/0nlytype/steps.css"
    }
  ],
  "css": {
    "@import \"../styles/0nlytype/steps.css\"": {}
  },
  "meta": {
    "movement": "VIII",
    "underneath": "native",
    "contract": "ot-steps"
  },
  "type": "registry:ui"
}
