Steps Section
block / marketing / motion
The "how it works" section as an ordered list in four rhythms — a connected horizontal row, a vertical timeline, alternating media and copy, or an editorial big-numeral list — with a connector that draws itself, a pulse that travels down it, a ring that closes around each marker, and display numerals that fill in as their row arrives.
- type
- block
- files
- 2
- registry deps
- skeleton, @automindz/motion-primitives
- npm deps
- lucide-react
installs
- components/automindz/steps-section.tsx
- styles/automindz-steps.css
- An <ol>, so the number is generated and aria-hidden
- The connector draws itself, then a slow pulse travels down it
- A ring draws round each marker; media settles as the copy lifts
- numbered: a display numeral fills on scroll, hover or focus — max() composes them
- Finished is the base state — no stylesheet, no motion, numerals still solid
steps has no default and no config file: a step is page copy, not business config, so it arrives as a prop next to the hero it follows. It renders an <ol> because the order is the meaning — the visible numeral is generated from the index and aria-hidden, since the list already announces position.
Two stylesheet imports, both once in your root layout: import "@/styles/automindz-steps.css" draws the connector, lands the markers and carries the flow pulse, and import "@/styles/automindz-motion.css" (from @automindz/motion-primitives) staggers the steps in. Skip either and that half simply does not move — the connector is drawn end to end, every marker ring is a closed circle, the media sits square, nothing is hidden waiting for a trigger. That is the same path a reduced-motion visitor, an engine without animation-timeline and motion={false} all take, and motion={false} emits no automindz- class at all.
What moves, once it can: the connector draws marker-centre to marker-centre, then a low-contrast highlight travels each segment on a 0.26Hz clock, offset per step so it reads as one signal running the chain rather than every segment throbbing at once. A hairline conic ring draws around each marker as its step arrives. In alternating the media frame settles **down** from above while the copy beside it is lifted **up** by Reveal — two things crossing over one window, which is the parallax read without any range that could strand. Hover and :focus-within tint the frame and zoom the artwork inside its crop, never the frame itself; those styles sit outside the motion guards, because which step is under the pointer is state, not a gesture.
layout="horizontal" holds one line from md (up to four steps) or lg (five or more), and the connector disappears with the row when it stacks. vertical runs the same rule down through the markers, one segment per step. alternating flips sides with grid placement and never with source order, so the DOM stays copy-then-media at every breakpoint. current and completed are for an onboarding-style use — the marketing case passes neither; current also gets a slow ring at 0.42Hz, off under reduced motion. Fewer than two steps renders nothing.
layout="numbered" is the editorial one: wide rows separated by a hairline, each led by a display-scale numeral zero-padded from the step count (01…09, then 10; a hundred steps widen every row rather than shunting the last few). There is no marker and no connector — every rule in the other three layouts is measured from a marker's centre, and drawing one into an empty column would point at nothing — so the rule between rows is a plain border. The numeral is generated and aria-hidden like the markers', and current / completed land on a word above the title instead of on it, since a numeral nobody hears is not a place to keep state. Below md the numeral sits above the copy at the small end of its clamp. Set the size with --automindz-steps-numeral-size; the default is clamp(2.75rem, 2rem + 4.5vw, 6rem).
The numeral fills from the baseline up as its row enters, and hover or :focus-within fills it early. The two are composed with max() rather than one replacing the other, so pointing at a row you have already scrolled past does nothing and moving the pointer away can never un-fill one. **The hollow state exists only inside the same @supports (animation-timeline: view()) and prefers-reduced-motion: no-preference guards as the fill that ends it**, plus a forced-colors / prefers-contrast: more floor that puts solid ink back: an outline nobody fills is a number nobody can read, so every degraded path renders it solid.
Every dial is a custom property: --automindz-steps-flow-duration (3.8s), -flow-stagger (0.42s), -flow-length (34%), -flow-strength (60%), -flow-angle (90deg), -ring-width (2px), -ring-gap (3px), -ring-strength (45%), -media-drift (-1.5rem), -media-scale (1.04), -media-zoom (1.03), -media-bloom (2.5rem), -hover-ms (240ms), -numeral-size (clamp(2.75rem, 2rem + 4.5vw, 6rem)), -numeral-stroke (0.028em), and -accent, which defaults to your own --primary.
Variations
one state per section
- 01Horizontal, three steps
- 02Horizontal, six steps
- 03Horizontal, two steps
- 04Vertical, mid-flow
- 05Alternating
- 06Numbered — the editorial list
- 07Numbered, mid-flow
- 08Numbered, motion={false} — the solid numeral
- 09Numbered, loading
- 10Static — reduced motion, no stylesheet, motion={false}
- 11Loading
- 12One step
Horizontal, three steps
Scroll it: the cards stagger in, the rule draws itself from the first marker toward the last, and a ring draws round each marker — all four over the same window as the step they belong to, off one shared animation-range. The geometry is centre-to-centre, so the rule can neither start before the first marker nor overshoot the last, and a scale can only ever shorten it. Then watch it at rest: a slow highlight travels down each segment on a clock, offset per step so it reads as one signal running the chain rather than every segment throbbing at once. Narrow the frame past md and the row stacks — the rule is display:none there, and a display:none element runs neither the draw nor the pulse.
From registry to running app
Four commands and a config file, in the order you actually do them.
Point components.json at the registry
One line in the consumer app adds the @automindz namespace. No token, no private npm scope.
About a minuteInstall the items you need
npx shadcn add @automindz/auth-card pulls the block, its registry dependencies and its config in one pass.
One command per itemEdit the config, not the component
Providers, tiers and FAQs land in lib/automindz as typed data. A client change is a data change.
Horizontal, six steps
The same geometry and the same sweep at twice the count, because the rule is per-pair rather than one line stretched behind the row. Six steps hold their single row from lg instead of md — grid-flow-col with auto-cols-fr guarantees one row, which is the guarantee the connector depends on. The stagger tightens rather than trailing off: five gaps of 11.25% is 56.25% of the row's entry phase, just under the 60% cap, and the last card still lands at exactly entry 100%. This is also the count that shows the flow pulse as a train: 3.8s a pass at a 0.42s offset per segment, so several are lit at different phases and the eye follows one down the row.
The whole path, six steps
Point components.json at the registry
One line in the consumer app adds the @automindz namespace. No token, no private npm scope.
About a minuteInstall the items you need
npx shadcn add @automindz/auth-card pulls the block, its registry dependencies and its config in one pass.
One command per itemEdit the config, not the component
Providers, tiers and FAQs land in lib/automindz as typed data. A client change is a data change.
Wire the callbacks
onSignIn, onCheckout, onSave — every one is awaited, so the pending, failure and retry states come with the item.
Check the states you did not write
Loading skeletons at the real height, empty distinguished from failed, disabled explained rather than silent.
Ship it
The item is yours from here: it is your code, in your repo, on your tokens.
Horizontal, two steps
The other end of the range. One segment, still centre-to-centre, and the second half of the row has no rule after it. Long titles do not move it — the marker is a fixed-size box, so the rule's offset never depends on how many lines the copy takes.
Two steps, one rule
Point components.json at the registry
One line in the consumer app adds the @automindz namespace. No token, no private npm scope.
About a minuteShip it
The item is yours from here: it is your code, in your repo, on your tokens.
Vertical, mid-flow
The onboarding case: completed steps become a check with sr-only text, and current carries aria-current="step" plus a slow ring at 0.42Hz — an order of magnitude under the 3Hz flash threshold, and gone entirely under reduced motion. The current marker is the one that does not get the drawn sweep ring: it already wears a static ring and that pulse, and a third circle on one disc is a bullseye. Here each step drives its own segment from its own timeline, so the rule fills one step at a time however long the list runs, and the pulse runs downward through the markers. Neither prop is needed for the marketing case.
Where you are
- Completed
Point components.json at the registry
One line in the consumer app adds the @automindz namespace. No token, no private npm scope.
About a minute - Completed
Install the items you need
npx shadcn add @automindz/auth-card pulls the block, its registry dependencies and its config in one pass.
One command per item - Completed
Edit the config, not the component
Providers, tiers and FAQs land in lib/automindz as typed data. A client change is a data change.
Wire the callbacks
onSignIn, onCheckout, onSave — every one is awaited, so the pending, failure and retry states come with the item.
Check the states you did not write
Loading skeletons at the real height, empty distinguished from failed, disabled explained rather than silent.
Alternating
Media and copy swap sides down the page, and the DOM stays copy-then-media on every row: only the grid placement flips. Swapping the JSX or reversing the flex row would put the artwork before the sentence explaining it for half the page, for a screen reader and for Tab. Below md there is one track and the placement does nothing. Scroll a row in slowly and watch the two columns: the copy is lifted from below by Reveal while the frame settles down from above and scales back from 1.04 with a bloom arriving under it — opposite directions over one window is the parallax read, bought without any range that can strand at the bottom of a page. Then hover a row, or Tab into one: the frame's border tints and the artwork zooms inside it, never with it, so nothing beside it moves.
How it works
Three moves, each one shown.
Point components.json at the registry
One line in the consumer app adds the @automindz namespace. No token, no private npm scope.
About a minuteStep 1 screenshotInstall the items you need
npx shadcn add @automindz/auth-card pulls the block, its registry dependencies and its config in one pass.
One command per itemStep 2 screenshotEdit the config, not the component
Providers, tiers and FAQs land in lib/automindz as typed data. A client change is a data change.
Step 3 screenshot
Numbered — the editorial list
Wide rows, a hairline between them, and a display numeral that arrives hollow and fills from the baseline up as its row enters. Scroll the real page rather than this frame: a preview that is fully visible has already finished, which is exactly what a fully-visible section is supposed to do. Then hover a row, or Tab into one — the fill jumps to full and stays there, because the scroll and the pointer are composed with max() rather than one replacing the other, so pointing at a row you already scrolled past does nothing at all. No marker and no connector here: every rule in the other three layouts is measured from a marker's centre, and drawing one to an empty column would be a diagram pointing at nothing.
How it works
Four moves, in the order you actually do them.
Point components.json at the registry
One line in the consumer app adds the @automindz namespace. No token, no private npm scope.
About a minuteInstall the items you need
npx shadcn add @automindz/auth-card pulls the block, its registry dependencies and its config in one pass.
One command per itemEdit the config, not the component
Providers, tiers and FAQs land in lib/automindz as typed data. A client change is a data change.
Wire the callbacks
onSignIn, onCheckout, onSave — every one is awaited, so the pending, failure and retry states come with the item.
Numbered, mid-flow
current and completed in a layout with no marker to put them on. They land on a word above the title instead — visible to everyone, so nothing rides on the colour of a numeral that is aria-hidden decoration. The completed label is read aloud because nothing else announces a finished step; the current one is aria-hidden because aria-current="step" on the row already said it.
Where you are
Completed
Point components.json at the registry
One line in the consumer app adds the @automindz namespace. No token, no private npm scope.
About a minuteCompleted
Install the items you need
npx shadcn add @automindz/auth-card pulls the block, its registry dependencies and its config in one pass.
One command per itemCompleted
Edit the config, not the component
Providers, tiers and FAQs land in lib/automindz as typed data. A client change is a data change.
Wire the callbacks
onSignIn, onCheckout, onSave — every one is awaited, so the pending, failure and retry states come with the item.
Check the states you did not write
Loading skeletons at the real height, empty distinguished from failed, disabled explained rather than silent.
Numbered, motion={false} — the solid numeral
The most important frame on this page. A numeral has no honest hollow resting state the way a heading does — an outline nobody fills is a number nobody can read — so the emptying lives inside the same @supports and reduced-motion guards as the fill that ends it. No stylesheet, no animation-timeline, reduced motion and motion={false} all land here: solid ink, full contrast, no stroke.
How it works
Four moves, in the order you actually do them.
Point components.json at the registry
One line in the consumer app adds the @automindz namespace. No token, no private npm scope.
About a minuteInstall the items you need
npx shadcn add @automindz/auth-card pulls the block, its registry dependencies and its config in one pass.
One command per itemEdit the config, not the component
Providers, tiers and FAQs land in lib/automindz as typed data. A client change is a data change.
Numbered, loading
The numeral block is sized in em against the numeral's own font size, so the skeleton row is the height the real row will be at any viewport — a rem guess here is the layout jump the skeleton exists to prevent. Same padding, same hairline, same two tracks.
How it works
Static — reduced motion, no stylesheet, motion={false}
The state this docs site exists to prove, and the one every degraded path lands on: the rule drawn end to end, every marker at full size and unringed, every frame flat and in place, every step composed. An engine without animation-timeline and an install that skipped the two stylesheet imports get exactly this. So does a reduced-motion visitor, with one deliberate difference — they keep the hover and focus-within affordance, because which step is under the pointer is state, not a gesture; only the transition into it is dropped. The travel is only ever added to this, never subtracted from it, so there is no configuration in which the connector is half drawn or a frame is left mid-drift.
From registry to running app
Four commands and a config file, in the order you actually do them.
Point components.json at the registry
One line in the consumer app adds the @automindz namespace. No token, no private npm scope.
About a minuteInstall the items you need
npx shadcn add @automindz/auth-card pulls the block, its registry dependencies and its config in one pass.
One command per itemEdit the config, not the component
Providers, tiers and FAQs land in lib/automindz as typed data. A client change is a data change.
Loading
The same grid, the same marker box and the same three text lines, so the copy landing under it moves nothing. No reveal and no draw here — a skeleton that animates in is a second wait on top of the one it is announcing. Hidden from the accessibility tree because the section already says aria-busy.
From registry to running app
One step
Nothing renders, heading included. A one-step "how it works" is a bug in the caller's copy, and drawing a lone numbered card with a connector to nowhere would hide it.
