Flow Diagram
block / motion
Columns of nodes joined by curves with light travelling along them — every coordinate computed from a declared layout, so it renders in a server component with nothing measured.
- type
- block
- files
- 3
- registry deps
- skeleton
- npm deps
- lucide-react
installs
- lib/automindz/flow.ts
- components/automindz/flow-diagram.tsx
- styles/automindz-flow.css
- Declared, not measured — no refs, no ResizeObserver, no client boundary
- Curves start on a node's border and end on the next one's, never mid-box
- One clock, one phase per edge — the wave hands off at each column
- Only adjacent columns connect; the rest land in skipped with a reason
- Import styles/automindz-flow.css once in your layout
Edit lib/automindz/flow.ts, not the component, and pass it — config has no default here (<FlowDiagram config={FLOW} />). The stylesheet is not optional: add import "@/styles/automindz-flow.css" to your root layout once, or nothing travels. Skipping it is safe, never blank — every node is still placed and every connector still drawn end to end, which is also the prefers-reduced-motion path. Every coordinate comes from the declaration, so there is no ref, no ResizeObserver and no "use client"; the frame is locked to the viewBox's aspect ratio, and that is what keeps the lines attached to the nodes at any container width. Only edges between **adjacent** columns are drawn — anything else lands in resolveFlow's skipped array with a reason rather than being routed through a node. Pass label to name the scroll region: below --automindz-flow-column (13.5rem per column) the diagram scrolls instead of squashing, and descriptions drop out below 52rem. It fills its container, so give a two-column flow a max-w-*. Dials: --automindz-flow-duration, --automindz-flow-beam, --automindz-flow-column.
Variations
one state per section
- 01The default: sources, hub, destinations
- 02Four columns, three hops, two edge labels
- 03Retinted, and slower
- 04Loading, at the diagram's real height
- 05Edges it refuses to draw
- 06One node, an empty column, no headings
- 07Standing still is the base state
The default: sources, hub, destinations
Watch one connector rather than the diagram: the beam crosses it, and the moment it lands on the hub the three on the right set off. That hand-off is arithmetic — every beam runs the same duration and differs only by a phase, so the wave cannot drift apart however long the page is open. Four sources against one hub, and the hub sits opposite the middle of the four because each column's block is centred independently.
Your stack
The platform
Where it lands
Four columns, three hops, two edge labels
One more column makes the cycle three stages instead of two, and the dash period follows it, so each hop still owns exactly its own third of the pass — nothing was retimed by hand. A fourth column also makes the box wider and therefore shorter, so this one passes a taller layout: the numbers are ratios, not pixels, and only their proportion matters. The two chips sit on the curve's midpoint and each carries a visually hidden “Uploads to Schema checks”, so they are not loose adverbs at the end of the reading order.
Ingest
Validate
Enrich
Serve
Retinted, and slower
className='text-primary' is the whole theming story: rails and beams are currentColor, the cards are the consumer's own card and border tokens, and nothing in the item names a colour — so on a neutral shadcn theme this looks the same, and on a themed one it is the accent. Nine seconds instead of five is one custom property and no re-render, because the per-edge value is a phase rather than a delay in seconds.
Your stack
The platform
Where it lands
Loading, at the diagram's real height
The skeletons are at the exact geometry of the nodes they stand in for — same centres, same widths, same height — because the layout was never data-dependent in the first place. Nothing moves when the real thing lands, and no connector is drawn to a box that is not there yet.
Your stack
The platform
Where it lands
Edges it refuses to draw
Six edges declared, three drawn. The one from Meters straight to Time series would have to cross the Collector, a typo'd id has nothing to attach to, and one runs backwards — all three land in resolveFlow's skipped array with a reason instead of being drawn through a node. Routing around an obstacle is a graph layout engine, and a layout engine cannot promise its geometry before layout.
Sources
Collector
Storage
One node, an empty column, no headings
The middle column is declared with no nodes, so it drops out rather than reserving an empty band — which makes the two survivors neighbours, and their edge legal. The right-hand column has a single node and no icon. No column carries a label, so the heading strip is not rendered at all rather than rendered blank. Constrained with max-w-lg, because a diagram with fewer columns fills whatever width it is given and a two-column flow across a full page is enormous.
Standing still is the base state
Turn on Reduce Motion in your OS and reload: every connector stays drawn end to end, from its source's border to its target's, and only the light stops. That is also what an install renders with the stylesheet import forgotten, because the rail is a presentation attribute and the beam's resting stroke is none. Narrow the window until a column would drop under 13.5rem and the diagram scrolls rather than squashing — the descriptions drop out first.
Your stack
The platform
Where it lands
