# DATAVIZ.md — animated charts & diagrams > **Scope**: a native data-visualization layer over the sd token system. 14 chart components + 10 diagram components, all reconstructed by hand (no chart library), animated in the sd style (presentational: animate once on viewport entry, then static), and degrading to a final static state under `prefers-reduced-motion`. Built on `motion/react` + SVG. Read `RULES.md` (color, type) first; this file adds the palette carve-out, the animation catalog, the component list and the layout helpers. ## When to use this layer Use it whenever a deck, landing or report needs a chart or a diagram **in the system's visual language** — instead of pasting a screenshot, embedding a third-party chart widget, or reaching for a chart library. Every piece is a plain React component that takes data and renders responsive SVG (or, for `BarChart`/`DonutChart`/`IndexRoll`, tokenized DOM). Pieces animate themselves on scroll-in; there is nothing to wire. Principle: **reconstruct natively**. No `recharts`/`chart.js`/`d3` renderer, no `` of a chart. Geometry (scales, paths, sankey/treemap/tree layout) is hand-rolled in `components/chart-utils.ts` and `components/layout.ts`, both dependency-free. ## Palette — hybrid (a scoped carve-out of "one accent") The base law is **one accent only** (`RULES.md`, `ANTIPATTERNS.md`). Data-viz needs more than one distinguishable color, so this layer adds a **scoped** carve-out, exactly like the event deck's partner hue: - **Ordered / intensity data** (heatmap, treemap, a single series) → a **sequential ramp** derived from the system (ink-100 → accent-400 → accent-600) via `seqRamp(t)` in `chart-utils.ts`. No new tokens. - **Categorical series** (radar, sankey, multi-line, venn, mind-map branches) → a short categorical set, **5 tokens max**, desaturated so they never compete with the accent: ```css --cat-1: var(--accent-500); /* #facc0d */ --cat-2: var(--series-2); /* #1b5566 teal */ --cat-3: #9a6a4f; /* clay */ --cat-4: #5b6b7a; /* slate */ --cat-5: #7d7a3f; /* olive */ ``` Resolve a series color with `catColor(i)` (cycles cat-1..5). Rules: - The categorical palette is **data-viz only**. Never use `--cat-*` for slide chrome, headings, backgrounds or UI. Outside a chart/diagram, "one accent" still holds. - Prefer the sequential ramp when the data is ordered; reach for categorical only when identity (not magnitude) is the point. - Semantic colors (`--success/warning/error/info`) stay reserved for status meaning, not category. ## Animation catalog (presentational) Every component shares one motion contract, so a deck of charts feels like one system: - **Trigger**: `useInView(ref, { once: true, amount: 0.4 })` via the shared `ChartFrame`. Animate once when scrolled into view, then hold the final state. Reload to replay. - **Reduced motion**: each component reads `useReducedMotion()` (forwarded by `ChartFrame` as `reduce`) and renders the **final** state with no transition. - **Easings** (from `MOTION.md`): entries `[0.22, 1, 0.36, 1]`; bar/column growth `[0.77, 0, 0.175, 1]` (ease-strong). Never `ease-in`, never bounce. - **Per-type motion**: - Bars / columns / histogram / gantt / funnel / waterfall → `scaleX`/`scaleY` grow from the baseline, staggered ~100–120 ms. - Lines / areas / radar / sankey / all diagram connectors → stroke **draw** via `pathLength` 0→1 (the same idea as the `.draw` helper); points/fills land after. - Pie / donut → sectors sweep open (donut is two-phase: ink wedge grows, then accent fills). - Scatter / bubble / treemap / heatmap → points/cells `scale`+`fade` in a stagger (diagonal cascade for the heatmap). - Diagram nodes → fade/scale in by depth or column; edges draw with `pathLength`; arrowheads via SVG ``. Stagger stays within the `MOTION.md` budget (30–200 ms between siblings). Charts never loop. ## Components All take typed `data`/`series` props, an optional size, and `className`. Charts render through `ChartFrame` (responsive `viewBox`, scales to container width). Import from the paths in the `INSTALL.md` / `SKILL.md` mapping (`components/charts/*`, `components/diagrams/*`). ### Charts (14) | Component | Shape of data | Notes | |---|---|---| | `BarChart` | `{label, value, display?}[]` | tokenized DOM bars; reference bar takes the accent | | `DonutChart` | `value` (0–100) + `centerValue` | two-phase conic donut | | `PieChart` | `{label, value}[]` | sectors sweep; `innerRatio` > 0 → donut | | `LineChart` | `{label, data:{x,y}[]}[]` | multi-series, `pathLength` draw, `smooth?` | | `AreaChart` | `{label, data:{x,y}[]}[]` | bottom-up clip wipe + line | | `ScatterPlot` | `{label, data:{x,y}[]}[]` | staggered points | | `BubbleChart` | `{x,y,size,label?}[]` | radius ∝ size, bubbles inflate | | `Histogram` | `{label, value}[]` | contiguous bars | | `BoxPlot` | `{label,min,q1,median,q3,max}[]` | whiskers draw, box grows | | `Heatmap` | `rows, cols, values[][]` | sequential ramp, diagonal cascade | | `Treemap` | `{id,label,value}[]` | deterministic binary split | | `RadarChart` | `axes, series:{label,values[]}[]` | multi-series polygons | | `FunnelChart` | `{label, value}[]` | trapezoid stages | | `WaterfallChart` | `{label, value, type?}[]` | floating bars + connectors | ### Diagrams (10) | Component | Shape of data | Notes | |---|---|---| | `Flowchart` | `nodes:{id,label,kind?,col,row}[], edges` | start/process/decision/end shapes | | `GanttChart` | `tasks:{label,start,end}[], ticks?, today?` | time-scaled bars | | `Sankey` | `columns, nodes, links:{source,target,value}[]` | width ∝ value | | `ERDiagram` | `entities:{id,title,fields,x,y}[], relations` | entity cards + relations | | `SequenceDiagram` | `actors, messages:{from,to,label}[]` | lifelines + ordered messages | | `NetworkGraph` | `nodes, edges:{source,target}[]` | deterministic circular layout | | `OrgChart` | `root` (tree) | tidy top-down | | `DecisionTree` | `root` (tree, `edgeLabel`) | decisions vs outcomes | | `MindMap` | `root` (tree) | radial branches | | `VennDiagram` | `sets[2..3], intersection?` | multiply blend | ## Layout & geometry helpers Reuse these instead of reinventing math (both dependency-free, deterministic — no `Math.random`, SSR-safe): - `components/chart-utils.ts` — `linearScale`, `niceTicks`, `linePath`, `smoothPath` (Catmull-Rom), `areaPath`, `sectorPath`, `polygonPath`, `polarToXY`, `catColor`, `seqRamp`, `lerpHex`, `clamp`. - `components/layout.ts` — `treeLayout` (org/decision), `radialLayout` (mind map), `sankeyLayout`, `treemapLayout`, `circleLayout` / `gridLayout` (network). - `components/chart-frame.tsx` — `ChartFrame`: responsive SVG wrapper + the in-view / reduced-motion contract. Every chart/diagram renders through it. ## Reproducing / extending - New chart? Wrap `ChartFrame`, build geometry with `chart-utils`, animate with `motion/react` + the easings above, and gate on the forwarded `{ inView, reduce }`. Keep it presentational. - Need a new series color? Add at most `--cat-1..5`; do not grow the palette. If you need more categories than 5, the chart is probably doing too much — split it. - Do **not** add a chart library to "save time". The native reconstruction is the point.