Flow Lanes
Show where a stream goes: one source fanning out to a lane per destination, each a stream of particles as dense as its rate.
import { FlowLane, FlowLanes } from '@youj/react'
Installation & usage
npm i @youj/reactFor an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.
Use flow lanes for one stream split many ways, where the split is the story: a model router's traffic by model, a load balancer's pools, a queue's consumers, an inbox's routing rules. The Routing flow card is built on it. For a run of steps with branches use a Flow Graph; for shares without a source, a Share Bar.
import { FlowLanes } from '@youj/react'
<FlowLanes
source="Gateway"
label="Requests by model"
lanes={[
{ id: 'pro', label: 'Kestrel 3 Pro', value: 47, tone: 'chart-1' },
{ id: 'orrery', label: 'Orrery 2', value: 137, tone: 'chart-2' },
]}
formatValue={(v) => `${Math.round(v)}/s`}
/>source names the node the lanes leave from. Each lane's value is its rate in any unit: it sets how many particles ride it (a share of the whole, at least one for any flow), the weight of its line and the share spoken with it. Lanes take the chart colors in order unless they set a tone. formatValue writes a rate with its unit. The particles stop while the chart is off screen or the tab is hidden, and paused stops them for good. No lanes, or nothing flowing, shows the empty state.
Description
A made-up delivery hub's parcels per minute, by route. Each lane's particles cross at the same speed, so the busy Harbor run is a dense stream and the Hill towns a trickle; the line's weight says the same.
active lights one lane and quiets the rest, and onActiveChange reports where pointing and focus go, so a table or a set of buttons beside the chart can light a lane too.
Code
flow-lanes-demo.tsx, the Demo story. The component itself comes from the package.
Reference
Propssource, lanes, active, onActiveChange, paused, formatValue…
Props
| Prop | Type | |
|---|---|---|
sourceRequired | stringThe node every lane leaves from, named over it: "Gateway". | – |
lanesRequired | readonly FlowLane[] | – |
active | string | nullThe lane to light, by id (controlled); | – |
defaultActive | string | nullThe lane lit at first (uncontrolled). default null | null |
onActiveChange | (id: string | null) => voidCalled when pointing or focus moves to a lane, or off them ( | – |
paused | booleanStops the particles where they are. They also stop while the chart is off screen or the tab is hidden. default false | false |
formatValue | (value: number) => stringWrites a rate with its unit: "57/s". Default: the locale's number format. | – |
locale | stringDefault: the YoujProvider locale. | – |
label | stringAccessible name. Default: the labels' "Flow lanes". | – |
config | Readonly<Record<string, { color?: string }>>A ChartConfig shared with your other charts: a lane whose id is a key takes its color, so one series matches across chart types. | – |
Other props pass through: extends ComponentProps<'div'>. | ||
StylingParts and data attributes
Styling
Parts: flow-lanes, flow-lanes-plot, flow-lanes-source, flow-lanes-source-node, flow-lanes-lane, flow-lanes-stream, flow-lanes-particles, flow-lanes-particle, flow-lanes-list, flow-lanes-item, flow-lanes-label, flow-lanes-value, flow-lanes-summary and flow-lanes-empty. While a lane is lit, its lane, particles and item have data-state="active" and the others data-state="inactive". The lane pitch is --flow-lanes-pitch (32px, 28px compact).
MotionDraw, stream, reweight · reduced motion
Motion
| Moment | Tokens | ||
|---|---|---|---|
| Drawthe lanes draw out from the source when a third of the chart scrolls into view, a lane after another | duration.deliberateease.enter560ms · cubic-bezier(0.16, 1, 0.3, 1) | 560mscubic-bezier(0.16, 1, 0.3, 1) | |
| Streamparticles cross at one steady speed, a full width in six deliberate beats, fading in and out at the ends; they stop off screen | no token linear, looping | linear, looping | |
| Reweighta new rate thickens or thins the line and reseeds the stream | duration.slowease.standard340ms · cubic-bezier(0.2, 0, 0, 1) | 340mscubic-bezier(0.2, 0, 0, 1) | |
| Pointingthe other lanes and their streams quiet | duration.fastease.standard140ms · cubic-bezier(0.2, 0, 0, 1) | 140mscubic-bezier(0.2, 0, 0, 1) |
Reduced motion
Drawn in place; the particles rest on their lanes, spaced by the rate, so the density still reads.
Reduced motion
The lanes are drawn in place and the particles stand still on them, as many as the rate gives, so a busy lane still looks busy.
AccessibilityKeyboard, focus, screen readers
Accessibility
- A named
group(yourlabel, or the labels' "Flow lanes") described by the source, how many lanes it feeds and their total ("Gateway to 4 lanes, 312/s in all."). - The lanes are a list; each is read with its name, rate and share ("Orrery 2: 137/s, 44%").
- The names are one tab stop. The arrow keys,
HomeandEndmove between them, and focus lights the lane as pointing does. - The drawing and the particles are decorative; the list says the same in words. Color is never the only cue: every lane is named.
- Its built-in text comes from the labels, so
<YoujProvider labels>translates it.