# 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.