Component
Flow diagram
Steps and the connections between them on a pannable canvas, for automations and process maps.
Plain-text source, for agents and clipboard use: /components/source/flow.txt
When to use it
Shows how steps connect: a trigger, the conditions it passes through and the actions at the end. Nodes are buttons laid out on a grid of flow units, edges are curves with arrowheads in one SVG, and branch labels sit on their edges. Selecting a node highlights the edges that touch it. The canvas scrolls inside its frame, and a minimap shows where the viewport sits.
Examples
Automation
Final documents go out 30 days before departure. Select a node to see its connections; scroll the canvas and watch the minimap.
<div class="flux-flow" data-flux-flow>
<div class="flux-flow__viewport" role="region" aria-label="Automation: final documents" tabindex="0">
<div class="flux-flow__canvas" style="--flux-cols: 66; --flux-rows: 30">
<svg class="flux-flow__edges" viewBox="0 0 66 30" aria-hidden="true">
<defs>
<pattern id="flow-grid" width="1" height="1" patternUnits="userSpaceOnUse"><circle class="flux-flow__dot" cx="0.5" cy="0.5" r="0.06" /></pattern>
<marker id="flow-arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="0.6" markerHeight="0.6" markerUnits="userSpaceOnUse" orient="auto"><path class="flux-flow__arrow" d="M0 0L10 5L0 10z" /></marker>
<marker id="flow-arrow-active" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="0.6" markerHeight="0.6" markerUnits="userSpaceOnUse" orient="auto"><path class="flux-flow__arrow flux-flow__arrow--active" d="M0 0L10 5L0 10z" /></marker>
</defs>
<rect width="66" height="30" fill="url(#flow-grid)" />
<path class="flux-flow__edge" data-from="confirmed" data-to="check" d="M14 14.5 C16 14.5 16 14.5 18 14.5" marker-end="url(#flow-arrow)" />
<path class="flux-flow__edge" data-from="check" data-to="documents" d="M30 14.5 C32.5 14.5 32.5 7.5 35 7.5" marker-end="url(#flow-arrow)" />
<path class="flux-flow__edge" data-from="check" data-to="wait" d="M30 14.5 C32.5 14.5 32.5 21.5 35 21.5" marker-end="url(#flow-arrow)" />
<path class="flux-flow__edge" data-from="wait" data-to="documents" d="M41 19 L41 10" marker-end="url(#flow-arrow)" />
<path class="flux-flow__edge" data-from="documents" data-to="guide" d="M47 7.5 C49.5 7.5 49.5 7.5 52 7.5" marker-end="url(#flow-arrow)" />
</svg>
<span class="flux-flow__label" style="--flux-x: 32.5; --flux-y: 11" aria-hidden="true">Yes</span>
<span class="flux-flow__label" style="--flux-x: 32.5; --flux-y: 18" aria-hidden="true">No</span>
<ol class="flux-flow__nodes">
<li><button class="flux-flow__node" type="button" data-node="confirmed" style="--flux-x: 2; --flux-y: 12; --flux-w: 12; --flux-h: 5" aria-pressed="false"><span class="flux-flow__node-type">Trigger</span><span class="flux-flow__node-title">Booking confirmed</span><span class="flux-visually-hidden">. Then: Departs within 30 days</span></button></li>
<li><button class="flux-flow__node" type="button" data-node="check" style="--flux-x: 18; --flux-y: 12; --flux-w: 12; --flux-h: 5" aria-pressed="false"><span class="flux-flow__node-type">Condition</span><span class="flux-flow__node-title">Departs within 30 days</span><span class="flux-visually-hidden">. Yes: Send final documents. No: Until 30 days before departure</span></button></li>
<li><button class="flux-flow__node" type="button" data-node="documents" style="--flux-x: 35; --flux-y: 5; --flux-w: 12; --flux-h: 5" aria-pressed="false"><span class="flux-flow__node-type">Action</span><span class="flux-flow__node-title">Send final documents</span><span class="flux-visually-hidden">. Then: Assign tour guide</span></button></li>
<li><button class="flux-flow__node" type="button" data-node="wait" style="--flux-x: 35; --flux-y: 19; --flux-w: 12; --flux-h: 5" aria-pressed="false"><span class="flux-flow__node-type">Wait</span><span class="flux-flow__node-title">Until 30 days before departure</span><span class="flux-visually-hidden">. Then: Send final documents</span></button></li>
<li><button class="flux-flow__node" type="button" data-node="guide" style="--flux-x: 52; --flux-y: 5; --flux-w: 12; --flux-h: 5" aria-pressed="false"><span class="flux-flow__node-type">Action</span><span class="flux-flow__node-title">Assign tour guide</span></button></li>
</ol>
</div>
</div>
<div class="flux-flow__minimap" aria-hidden="true">
<svg viewBox="0 0 66 30">
<rect class="flux-flow__minimap-node" x="2" y="12" width="12" height="5" rx="0.5" />
<rect class="flux-flow__minimap-node" x="18" y="12" width="12" height="5" rx="0.5" />
<rect class="flux-flow__minimap-node" x="35" y="5" width="12" height="5" rx="0.5" />
<rect class="flux-flow__minimap-node" x="35" y="19" width="12" height="5" rx="0.5" />
<rect class="flux-flow__minimap-node" x="52" y="5" width="12" height="5" rx="0.5" />
<rect class="flux-flow__minimap-viewport" x="0" y="0" width="66" height="30" />
</svg>
</div>
</div>Anatomy
| Part | Description |
|---|---|
| Frame | A bordered layer-01 container with a scrolling viewport. |
| Canvas | Sized in flow units with --flux-cols and --flux-rows, under a dot grid. |
| Node | A button positioned by --flux-x, --flux-y, --flux-w and --flux-h, with a mono type label and a title. Selected nodes take surface-selected and a brand border. |
| Edge | An SVG path in border-strong with an arrowhead, in the same units as the nodes. Edges touching the selected node turn brand. |
| Edge label | A mono label at the midpoint of a branch, on the canvas colour so the line breaks behind it. |
| Minimap | The nodes in miniature and a frame for the visible area, in the bottom corner. Clicking it scrolls the canvas. |
API
| Name | Kind | Description |
|---|---|---|
flux-flow | class | The frame. data-flux-flow handles selection, the edge highlight and the minimap. |
flux-flow__viewport | class | The scroll container. Give it role="region", a name and tabindex="0". |
flux-flow__canvas | class | Sized by inline --flux-cols and --flux-rows in flow units. |
flux-flow__edges | __edge | __arrow | class | The SVG, with viewBox="0 0 cols rows", its paths and arrowhead markers. data-from and data-to name the nodes an edge joins. |
flux-flow__label | class | An edge label, positioned with unitless --flux-x and --flux-y. |
flux-flow__nodes | __node | __node-type | __node-title | class | The ordered list of node buttons, each with data-node, aria-pressed and inline unitless --flux-x, --flux-y, --flux-w and --flux-h. |
flux-flow__minimap | __minimap-node | __minimap-viewport | class | The overview, drawn in the same units. |
flux-flow-select | attribute | Event dispatched on the frame when the selection changes. detail.node is the button, or null. |
States
| State | Treatment |
|---|---|
| Default | layer-01 node with a border-strong hairline. |
| Selected | surface-selected, brand border, and brand edges in and out. |
| Scrolled | The minimap frame follows the visible area. |
Keyboard
| Keys | Action |
|---|---|
Tab | Moves through the nodes in list order, which is the order of the process. |
Enter / Space | Selects the node, or clears the selection if it was selected. |
Arrow keys | Scroll the canvas when the viewport has focus. |
Accessibility
- Edges are invisible to a screen reader, so each node carries hidden text naming what comes next: “Yes: Send final documents. No: Until 30 days before departure.” Write node titles so they read on their own after a branch label.
- List nodes in the order the process runs, not the order they were drawn.
- The minimap is hidden from assistive technology; the viewport scrolls with the keyboard.
- Selected state is a border and a ground change, and aria-pressed says it too.
Tokens
The tier-3 and tier-2 tokens this component binds to. Change the token, not the component.
- --flux-flow-unit | -height | -bg | -border | -grid
- --flux-flow-node-bg | -node-border | -node-padding | -node-type
- --flux-flow-node-selected-bg | -node-selected-border
- --flux-flow-edge | -edge-active | -edge-width | -label-bg
- --flux-flow-minimap-width | -minimap-bg | -minimap-node | -minimap-viewport
Do and don’t
Do
- Lay the flow out left to right, branching up and down.
- Label every branch out of a condition.
Don’t
- Do not colour nodes by type; the type label says it. Colour is for the selection.
- Do not cross edges when moving a node would avoid it.