KQ-1811 · ROOT/EXCHANGE

Root Exchange

A hub feeding a ring of nodes through fine, root-like paths: neighbours share a trunk that forks half way, each trunk as thick as the flow it carries, and pulses of light leave the hub along every route at that node's share of the total, so the busy ones visibly draw more. Hovering or tabbing to a node lights its route and shows its label, value and share in a tag that rises on springs.snap; a press reverses its flow, the route's direction springing through zero on springs.snap so pulses already in flight slow, stop and run home while the node kicks on springs.recoil and its chevron turns. Arrow keys walk the ring and Enter or Space reverses the focused node.

KQ-1811 · ROOT/EXCHANGE

Arrow keys move between nodes; Enter or Space reverses a node's flow.

reservoir · 6,310 l/s to 7 districts

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/root-exchange

Props

nodes
{ id: string; label: string; value: number }[]
default defaultRootNodes
The nodes the hub feeds; each value's share of the total sets its pulse rate and its route's thickness.
hubLabel
string
default "Hub"
What the hub is, for its accessible name and announcements.
format
(value: number) => string
default en-US grouping
Prints a node's value in its tag and accessible name.
reversed / defaultReversed
string[]
default []
Ids of the nodes whose flow runs back to the hub, controlled or initial.
onReversedChange
(ids: string[]) => void
default —
Fires from the press that reversed or restored a node, with the new ids.
onActiveChange
(id: string | null) => void
default —
Fires when hover or keyboard focus brings a node forward, or lets it go.
pulseRate
number
default 4
Pulses a second leaving the hub in all, shared out by value.
travel
number
default 1800
Time for a pulse to cross an average route, ms; every pulse moves at the same speed in pixels.
curve
number
default 0.55
How far the roots wander off a straight line, 0 (schematic) to 1 (gnarled).
rootlets
number
default 0.5
How many hair-fine side rootlets the routes shed, 0 to 1.
palette
"cobalt" | "signal" | "amber" | "ink"
default "cobalt"
The colourway, from the theme's tokens.
colors
{ route?: string; pulse?: string; hub?: string }
default —
CSS colours that replace parts of palette.
seed
number
default 7
Seeds where the nodes sit and how the roots wander.
paused
boolean
default false
Freezes the pulses where they are.
dpr
number
default 2
Cap on the pulse canvas's device pixel ratio, at most 2.
label
string
default "Flow from the hub"
The node group's accessible name.
disabled
boolean
default false
Nodes cannot be focused or reversed; the flow still runs.
className
string
default "h-56 w-full"
Sizes the box.