Data Storytelling

Diagram Kit

Eleven data-driven SVG diagrams — hub routing, sequence lanes, cycle, timeline, radial branches, funnel, tier pyramid, anchored callouts, branch reveal, quadrant and Venn — each drawn in with stroke tracing and staggered nodes.

This is a WebberUI Pro component

The install command below needs a licence to run; without one it returns 401.

How to install Pro components →See the plans →

The eleven diagrams that show up most in decks, explainer pages and product tours, all described as data: you pass nodes and relationships, the component handles layout and the entrance. They share one animation contract — links and outlines are traced in as strokes, nodes pop in following the topology, and hovering or focusing a node keeps only its related paths lit while the rest fade, with a tooltip for extra detail. The DiagramKit frame provides the title, description, legend and play / replay control, and each diagram is reached as a static property such as DiagramKit.Venn. Every diagram is an SVG whose viewBox scales to the container width, and CJK labels wrap automatically.

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/diagram-kit.en.json?t=<install token>"

New project or installation trouble? Start here →

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

700
90
Active edited usageDefault props
<DiagramKit/>

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/diagram-kit.en.json?t=<install token>"

Or, once registries are configured in components.json, install it as @webberui/diagram-kit.

Usage

import { DiagramKit } from "@/components/ui/diagram-kit";

<DiagramKit title="How the order hub routes work" legend={[{ label: "Inbound" }, { label: "Outbound" }]}>
  <DiagramKit.HubRouting
    hub={{ id: "hub", label: "Order hub", detail: "About 1,200 orders a day" }}
    spokes={[
      { id: "web", label: "Website", side: "in" },
      { id: "store", label: "Store", side: "in" },
      { id: "wh", label: "Warehouse", side: "out" },
      { id: "cs", label: "Support", side: "out" },
    ]}
  />
</DiagramKit>;

<DiagramKit trigger="mount" accent="#0ea5e9">
  <DiagramKit.Venn
    sets={[
      { id: "design", label: "Design" },
      { id: "eng", label: "Engineering" },
      { id: "biz", label: "Business" },
    ]}
    intersections={[
      { sets: ["design", "eng", "biz"], words: ["Usable", "Buildable", "Profitable"] },
    ]}
  />
</DiagramKit>;

Each diagram also works on its own without the DiagramKit frame; it then plays as soon as it mounts.

Props

DiagramKit

PropTypeDefaultDescription
titlestring—Diagram title
descriptionstring—Description under the title
legendDiagramLegendItem[]—Legend items; without a color they take the series palette in order
playingboolean—Controlled play state (true means the entrance has been triggered)
defaultPlayingbooleanfalseInitial play state in uncontrolled mode
onPlayingChange(playing: boolean) => void—Called when the play state changes
trigger"inView" | "mount" | "manual""inView"Entrance trigger: 25% scrolled into view, on mount, or only via the button / playing
drawDurationnumber700Duration of one stroke trace (ms); falls back to --wb-diagram-draw
staggernumber90Delay between nodes entering (ms); falls back to --wb-diagram-stagger
accentstring#3b82f6Accent color, written to --wb-diagram-accent
seriesstring[]accent + 4 colorsCustom palette; the first color is the main one
controlsbooleantrueShow the play / replay button
childrenReactNode—One diagram
classNamestring—Passed to the outermost container

Every diagram also takes interactive?: boolean (default true: hover / focus highlighting and tooltips), label?: string (the SVG's accessible name) and className?: string. The tables below list only each diagram's data props; the shared node type DiagramNode is { id, label, detail? }.

DiagramKit.HubRouting

FieldTypeDefaultDescription
hubDiagramNode—The central hub
spokesHubSpoke[]—Spoke nodes; side: "in" feeds in from the left, "out" routes out to the right
pulsebooleantruePulses travelling along the links once the entrance finishes

DiagramKit.SequenceLanes

FieldTypeDefaultDescription
lanesDiagramNode[]—Lanes (participants), left to right
messagesSequenceMessage[]—Messages in time order { from, to, label, detail? }; when from equals to a self-loop is drawn

DiagramKit.CycleBuild

FieldTypeDefaultDescription
stepsDiagramNode[]—Steps in cycle order; the last one links back to the first
centerstring—Title in the middle of the ring

DiagramKit.TimelineNodes

FieldTypeDefaultDescription
eventsTimelineEvent[]—{ id?, date, title, detail? }, in chronological order
orientation"auto" | "horizontal" | "vertical""auto"auto switches to vertical when the container is narrower than 480px

DiagramKit.RadialBranches

FieldTypeDefaultDescription
centerDiagramNode—Center node
branchesRadialBranch[]—First-level branches (with children as the second level), clockwise from the top

DiagramKit.FunnelAbsorption

FieldTypeDefaultDescription
stagesFunnelStage[]—{ id?, label, value, detail? }, top to bottom
particlesbooleantrueParticles drawn into the funnel and flowing down after the entrance
valueFormatter(value: number) => stringthousands separatorsValue formatter

DiagramKit.TierPyramid

FieldTypeDefaultDescription
tiersDiagramNode[]—Tiers; tiers[0] is the base and the last item is the apex

DiagramKit.AnchoredCallouts

FieldTypeDefaultDescription
anchorsCalloutAnchor[]—{ id, x, y, label, detail?, side? }; x and y are percentages 0–100 of the underlying content
childrenReactNode—The underlying content (a screenshot, card or illustration)

DiagramKit.BranchReveal

FieldTypeDefaultDescription
rootBranchNode—Tree root; children holds child nodes
expandedIdsstring[]—Controlled expanded node ids
defaultExpandedIdsstring[][root.id]Initially expanded nodes in uncontrolled mode
onExpandedChange(ids: string[]) => void—Called when the expanded set changes

With interactive={false} the whole tree is expanded and cannot be toggled.

DiagramKit.QuadrantPositioning

FieldTypeDefaultDescription
xAxisQuadrantAxis—Labels for both ends of the x axis { low, high }
yAxisQuadrantAxis—Labels for both ends of the y axis { low, high }
quadrants[string, string, string, string]—Quadrant names in the order top-left, top-right, bottom-left, bottom-right
itemsQuadrantItem[]—Controlled items { id, label, x, y, detail? }, x and y in 0–100
defaultItemsQuadrantItem[][]Initial items in uncontrolled mode
onItemsChange(items: QuadrantItem[]) => void—Called after an item is dragged or moved with the arrow keys

DiagramKit.Venn

FieldTypeDefaultDescription
setsVennSet[]—2 or 3 sets (only the first three are used)
intersectionsVennIntersection[][]{ sets, label?, words?, detail? }; with more than one entry in words they rotate
wordIntervalnumber1800Interval between intersection word swaps (ms)

Also exported by name: wrapText (CJK-aware line-wrap estimation), DIAGRAM_SERIES and all of the types above.

How it works

  • Shared animation contract: link and outline pathLength goes from 0 to 1 (easing [0.65, 0, 0.35, 1]), and nodes scale up from 0.6 with a fade on a spring (stiffness 320, damping 26), entering in topological order stagger apart
  • Timing variables: --wb-diagram-draw (700ms), --wb-diagram-stagger (90ms), --wb-diagram-accent (#3b82f6) and --wb-diagram-dim (0.25, the opacity of everything else while highlighting); props take precedence over the variables
  • Replay: the frame's Replay button remounts the diagram so the whole entrance runs again from the start
  • CJK wrapping: labels live in SVG text and wrap by estimated character width (CJK can break between any two characters, Latin text breaks between words, and punctuation is kept off the start of a line); text beyond the line limit ends in an ellipsis
  • Venn: the dashed outlines are traced in, then the circles spring together from apart, and the overlaps light up via clip-path fills; intersection words cross-fade every 1.8 seconds
  • Funnel: particles with a deterministic spread are drawn in from the top and some are "absorbed" at intermediate stages, matching each stage's drop-off; each stage shows its conversion rate from the previous one on the right
  • Anchored callouts: the underlying content can be anything; the component measures where it sits, splits labels between the left and right sides without overlapping, and connects each one to its anchor with an elbow leader line

Accessibility

  • Each diagram's SVG has a title and desc, plus an sr-only structured text alternative (a list or table) so screen readers get the full data
  • Nodes are focusable with Tab, and focusing one highlights its related paths just like hover; keyboard focus also draws an outline
  • Branch reveal nodes are role="button" with aria-expanded, toggled with Enter or Space (Enter during IME composition is ignored)
  • Quadrant items can be dragged, or focused and moved with the arrow keys (hold Shift to move by 10)
  • Tooltips are purely visual (aria-hidden); the same information is in the sr-only text
  • When the user has turned on reduce motion in their system settings, every diagram shows its final state straight away: no stroke tracing, no node pop-ins, no particles or pulses, and the Venn intersection words do not rotate