System Map Canvas
A pannable, zoomable system map: nodes and edges are generated from data, groups expand and collapse, and selecting a node highlights its connected paths and opens its details.
This is a WebberUI Pro component
The install command below needs a licence to run; without one it returns 401.
Turn services, databases, queues and external dependencies into an architecture diagram people can actually operate. Give it node and edge data and the component lays the nodes out in layers following the edge direction, on a blueprint-style grid with semantic edge colors and a legend. Drag to pan, scroll or pinch to zoom, and expand a group into a frame or collapse it back into a single card (edges into a collapsed group reconnect to the group itself). Select any node and its edges and neighbors stay sharp while everything else fades to 25%; a details panel slides in from the right listing upstream and downstream nodes, one click away. A minimap and a "fit to view" button sit in the bottom-right corner.
Bring this into your project
Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.
npx webberui@latest add "https://webberui.com/r/system-map-canvas.en.json?t=<install token>"Playground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<SystemMapCanvas/>
The complete example includes the preview layout and data. Install the component first, then paste this file into your project.
Installation
npx webberui@latest add "https://webberui.com/r/system-map-canvas.en.json?t=<install token>"Or, once registries are configured in components.json, install it as @webberui/system-map-canvas.
Usage
import {
SystemMapCanvas,
type SystemMapEdge,
type SystemMapNode,
} from "@/components/ui/system-map-canvas";
const nodes: SystemMapNode[] = [
{ id: "web", label: "Web 用戶端", kind: "client" },
{ id: "gateway", label: "API Gateway", kind: "service" },
{ id: "orders", label: "訂單域", kind: "group" },
{ id: "order", label: "訂單服務", kind: "service", parent: "orders" },
{ id: "queue", label: "通知佇列", kind: "queue", parent: "orders" },
{ id: "db", label: "主資料庫", kind: "database", description: "訂單的唯一真實來源" },
];
const edges: SystemMapEdge[] = [
{ from: "web", to: "gateway", kind: "sync", label: "HTTPS" },
{ from: "gateway", to: "order", kind: "sync" },
{ from: "order", to: "queue", kind: "async", label: "訂單事件" },
{ from: "order", to: "db", kind: "data" },
{ from: "order", to: "gateway", kind: "error" },
];
<SystemMapCanvas
nodes={nodes}
edges={edges}
defaultExpanded={["orders"]}
edgeStyle="curved"
onSelectedChange={(id) => console.log("selected", id)}
/>;The sample labels are Traditional Chinese: 用戶端 (client), 訂單域 (orders domain), 訂單服務 (order service), 通知佇列 (notification queue), 主資料庫 (primary database), 訂單事件 (order event).
Props
| Prop | Type | Default | Description |
|---|---|---|---|
nodes | SystemMapNode[] | — | Node list |
edges | SystemMapEdge[] | — | Edge list; edges that point at missing nodes are skipped |
layout | "auto" | "manual" | "auto" | auto layers nodes from the edges; manual uses each node's x / y (nodes without coordinates fall back to the automatic position) |
expanded | string[] | — | Controlled: ids of the expanded groups |
defaultExpanded | string[] | [] | Uncontrolled: ids of the groups expanded initially |
onExpandedChange | (expanded: string[]) => void | — | Called when a group expands or collapses |
selectedId | string | null | — | Controlled: selected node id |
defaultSelectedId | string | null | null | Uncontrolled: initially selected node id |
onSelectedChange | (id: string | null) => void | — | Called when the selection changes (null when cleared) |
minZoom | number | 0.3 | Minimum zoom |
maxZoom | number | 2.5 | Maximum zoom |
edgeStyle | "orthogonal" | "curved" | "orthogonal" | Edge style: orthogonal elbows or smooth cubic curves |
showMinimap | boolean | true | Whether the minimap starts open; the control bar button toggles it |
surface | "dark" | "auto" | "dark" | dark is a fixed dark blueprint look; auto follows the site's light / dark theme |
height | number | 420 | Canvas height (px) |
className | string | — | Passed through to the outer container |
SystemMapNode
| Field | Type | Default | Description |
|---|---|---|---|
id | string | — | Unique node id, referenced by edges and parent |
label | string | — | Name shown on the card |
kind | "service" | "database" | "queue" | "client" | "external" | "group" | "service" | Sets the icon and subtitle; any node with children is treated as a group |
parent | string | — | Id of the group it belongs to (groups can nest) |
description | string | — | Description shown in the details panel |
x | number | — | World x of the card's top-left corner when layout="manual" |
y | number | — | World y of the card's top-left corner when layout="manual" |
SystemMapEdge
| Field | Type | Default | Description |
|---|---|---|---|
id | string | — | Edge id; generated when omitted |
from | string | — | Source node id |
to | string | — | Target node id |
label | string | — | Small label at the edge midpoint |
kind | "sync" | "async" | "data" | "error" | "sync" | Semantic kind: sync call (solid), async message (flowing dashes), data read/write, error report (dotted) |
SystemMapNodeKind, SystemMapEdgeKind and SystemMapCanvasProps are exported as named types as well.
CSS variables
| Variable | Default | Description |
|---|---|---|
--wb-system-map-canvas-sync | #d4d4d4 | Sync call edge color (falls back to #a3a3a3 on the surface="auto" light theme) |
--wb-system-map-canvas-async | #2dd4bf | Async message edge color |
--wb-system-map-canvas-data | #f59e0b | Data read/write edge color |
--wb-system-map-canvas-error | #fb7185 | Error report edge color |
--wb-system-map-canvas-accent | #2dd4bf | Accent for the selected node, focus rings and the minimap frame |
--wb-system-map-canvas-flow | 1.2s | Duration of one cycle of the async dash flow |
How it works
- Self-written layered layout: with
layout="auto", each node's column comes from its topological depth along the edges, and every column is reordered with four alternating barycenter sweeps to reduce crossings. Clients (kind="client") are always treated as entry points in the leftmost column, error edges are drawn but not used for layering, and any remaining cycles are broken by skipping DFS back edges, so feedback loops do not skew the whole map - Groups are laid out recursively: an expanded group lays out its own children first, then joins the parent level as one large node, so group frames never overlap outside nodes. A collapsed group turns back into a single card; edges into any node inside it reconnect to the group, and duplicate edges merge into one
- Expand animation: children spread out from the group's center to their layout positions on a spring (stiffness 260, damping 26), with edge paths interpolating alongside; collapsing reverses this back into the group card
- Pan and zoom: drag the background to pan (pointer capture); the wheel zooms around the cursor, and trackpad pinch (ctrl + wheel) and two-finger touch work too. Moves from buttons, keys and the wheel are smoothed by a spring (stiffness 300, damping 35), while dragging tracks the pointer directly. The background grid follows the zoom, with a major line every 5 minor cells
- Minimap: shows every visible node plus the current view frame; drag the frame to move the view, or click outside it to recenter there first
- The async dash flow only animates while the component is on screen and pauses once it scrolls away
Accessibility
- The outer element is
role="application"witharia-roledescription="系統架構圖"(system map) and anaria-describedbypointing at the usage instructions - The canvas is focusable: arrow keys pan by 40px,
+/-zoom, and0fits the view. Tab visits every node (button) in column order, and focusing a node outside the view pans it into view - Enter selects a node, Space on a group expands or collapses it, and Esc clears the selection and closes the details panel. Each node's
aria-labelincludes its name, kind and upstream / downstream counts (for example "付款服務,服務,1 個上游,2 個下游" — payment service, service, 1 upstream, 2 downstream) - A
sr-onlylist of nodes and edges is also provided, reading each edge as "source to target, kind: label" - Every button has a
focus-visiblering; the legend and minimap are visual aids only (aria-hidden) - When the user's system has reduce motion turned on, async edges become static dashes that do not flow, and panning, zooming, expanding, collapsing and the details panel all jump straight to their end state
Spreadsheet Grid
An editable spreadsheet grid: arrow-key selection, double-click or Enter to edit, a formula bar showing raw input, simple formulas, and a frozen header row and column.
Knowledge Graph
A force-directed knowledge graph built from Markdown or structured records: a constellation-style dark canvas you can drag, zoom and search, with each node's source citations in a side panel.