Step ring

A numbered step badge: a quarter-arc spins for the current step, a tick for done, a dash for skipped.

Behaviour

  • The arc is a CSS spin on a quarter-length dash, with transform-box: fill-box so it turns about the circle's own centre; it pauses with animation-play-state while off screen.
  • The badge is opaque (bg-background) so a connector line behind it never crosses the number; the arc and the tick take the text colour.
  • Size it with className (size-7 text-xs by default): size-40 text-4xl scales the number with the ring.
  • Each ring carries a screen-reader label such as "Step 3, current"; the drawn number and icons are hidden from assistive tech.
  • StepRise rows fade and rise 6px in 450ms, each 70ms after the last; the delay stops growing after the 6th row so long lists don't wait.
  • A state change in place fades the badge colours (150ms) and the tick fades in from 90% scale (160ms).
  • Reduced motion: the arc turns at a third of the speed, a state change only fades its colours, and rows fade in without rising.

Props

PropTypeDescription
state"pending" | "current" | "done" | "skipped"StepRing: current spins a quarter-arc on the ring, done fills it with a tick, skipped shows a dash, pending shows the number.
indexnumberStepRing: the number drawn inside the ring for pending and current; fits 1-99 at the default size.
ordernumberStepRise: position in the list; the row starts order x stagger seconds after mount.
staggernumberdefault 0.07StepRise: seconds between one row and the next.

Installation

npx shadcn@latest add https://purrfectui.com/r/step-ring.json

How to use

import { StepRing, StepRise } from "@/components/ui/step-ring";

<ol>
  {steps.map((step, i) => (
    <StepRise key={step.key} order={i} className="flex items-center gap-3 py-4">
      <StepRing state={step.state} index={i + 1} />
      {step.label}
    </StepRise>
  ))}
</ol>

Source code

Loading…

License

MIT. Copy it into your project and change it as you like.

0