render-as-html · canonical primitives
Ten bones the whole system is built on.
Charts and tables that show up across the page shapes, locked to the one-truth palette, with their interaction contracts and anti-patterns written down once. Each frame below is the reference implementation — open it standalone, copy it, fork it.
10 of 10 primitives
01 · chart
Donut — categorical status mix
A one-glance "shape of the population" summary that pairs above a dense table. The legend doubles as a cross-highlight filter — clicking a row isolates that segment and surfaces a visible × clear control.
- pick when
- You have 2–5 mutually-exclusive categories and the proportion is more useful at a glance than the absolute counts.
- required
- Subject-count in the center (never a category count); ring track in
--rule-soft; legend in CSS subgrid so counts and percents align across rows. - interaction
- Click a legend row to isolate that segment; visible
× clearappears when a filter is active. - avoid
- 6+ slivers (use a stacked bar instead); donut-on-its-own as a hero — it's always paired with the data beneath it.
02 · chart
Bar — ranked top-N
Used when the question is "which N are biggest, and by how much." Bar length carries magnitude; the name lives in its own subgrid column to the left so long identifiers stay legible. Ochre marks the current leader; terracotta the rest.
- pick when
- Showing ≤12 items where the order matters and a single magnitude per item is the signal.
- required
- Name column to the left of the bar (never on the bar with text-shadow); ochre leader, terracotta rest; bars use a subgrid that aligns name / bar / value / Δ across rows.
- interaction
- Sort dropdown: at minimum value, alpha, and the secondary dimension (Δ, age, etc.). Bars re-scale when the sort changes.
- avoid
- Text-shadow / outline glow on labels; rainbow per-bar coloring; horizontal bars without a max-width cap.
03 · chart
Sparkline — stat-tile cluster
2–4 hero metrics, each with its current value, its delta vs. a prior period, and the shape of its recent trend. Tile cluster sits above the dense table; the sparkline carries direction-of-trend without becoming a separate chart.
- pick when
- The metric IS the subject (not an artifact count); current value + shape-of-recent-history is what the reader needs in one glance.
- required
- Single accent color for the spark line; delta carries direction-of-good (ok / accent / muted); latest-point dot is hollow (paper fill, accent stroke) so it reads as a marker, not a glitch.
- interaction
- Hover the spark to drop a vertical guide + value/time tip at the nearest data point.
- avoid
- 4+ tiles (cross into dashboard territory); stat tiles that count the artifact's own production; axis tick marks for every data point (the tooltip carries precision).
04 · chart
Stacked bar — composition over time
When you need total volume and category mix across periods in one figure. Bars carry the trend; segment colors carry the mix. The legend is a chip filter that re-stacks bars when you remove a category — so dropping Success lets you see the failure rhythm.
- pick when
- Categories sum to a meaningful whole each period (success/fail buckets, traffic mix, deal stage); 7–30 periods on the x-axis.
- required
- Y-axis recomputes when categories toggle off; severity-ascending bottom-up stack order; per-column tooltip with the day's breakdown + total; chip legend doubles as the filter.
- interaction
- Hover any column for the full breakdown tooltip; click chips to add/remove categories.
- avoid
- Categories that don't sum cleanly (use grouped bars instead); fixed y-axis when categories can toggle off.
05 · chart
Topology — inline service graph
When connections are the point and the node count is ≤30. Hand-positioned coordinates (force-directed simulations earn their place at 30+ nodes); straight edges meet rect boundaries; click-to-focus dims everything outside the neighborhood.
- pick when
- Reader needs to trace connections, not scan a list; node count fits a single viewport hand-positioned (≤30).
- required
- Mono-uppercase node labels; paper-card node fill with a cluster-colored dot (never fill the whole node with a category color); right-rail inspector matching the editorial-shape entity pattern; edges meet rect boundaries.
- interaction
- Click a node to focus its neighborhood (dim others, light direct edges, update inspector); search to ochre-highlight matches independent of focus;
× clear focusresets. - avoid
- Force-directed wiggle on small graphs; edges drawn center-to-center poking out of nodes; an unstyled SVG with no surrounding chrome.
06 · table
Dense ops table
The instrument-register workhorse: scan results, inventories, port lists. Mono first column for identifiers, sortable headers, status pills carrying outline + dot + label (so colorblind viewers still parse via shape and text), footer rows with provenance.
- pick when
- The dataset is a flat list of records keyed by an identifier; ≥6 rows; filtering / sorting is the central interaction.
- required
- Mono first column (identifier); sans middle (labels); mono right-aligned numerics with
tabular-nums; pills with outline + dot + text (no color-only); footer reports true row count (not filtered). - interaction
- Click column header to sort (asc → desc → none cycle); pill-filter in toolbar with active state shown by color inversion; search across identifier + label fields.
- avoid
- Color-only status; footer that lies about the dataset size when filtered; sorts with no neutral state.
07 · table
Comparison matrix — items as columns
The axis flip is the shape. Items get columns; criteria get rows; a weight column on the far left tunes the aggregate live. Star marks the per-row winner; the aggregate row marks the overall. The +/− stepper is full-height so weight tuning is one click, not a fiddly spinner.
- pick when
- "X vs Y vs Z" decision matrix with shared criteria; you want the reader to tune weights and see who wins; 2–5 items, 3–10 criteria.
- required
- Items as columns (entities-as-rows is a dashboard, not this); weight column on the far left with a full-height +/− stepper flanking the input; aggregate row normalized to 0–100; per-criterion direction (
↑ better/↓ better) is explicit; round-tripcopy as promptwith visible textarea fallback. - interaction
- Step or type any weight 0–5; per-row winner + overall winner recompute on every change.
- avoid
- Entities-as-rows layout; tiny native spinner buttons; weights without bounds; copy buttons that imply another canonical format.
08 · table
Annotated diff — code review primitive
Findings sit in the diff, anchored to the line they touch — not in a sidebar that loses spatial context. Severity travels three ways (left bar + badge fill + strip count) so the signal survives colorblind viewing. Syntax highlighting is a tiny set of local CSS classes — no Prism, no CDN.
- pick when
- Reviewing code changes, security findings, or any line-anchored critique where adjacency to the source matters more than a side panel.
- required
- Severity carried three ways (strip count + left bar + badge); inline annotations with explicit
L· new/L· removedlabels; +/− gutter chars in their own column AND row background tint; per-findingcopy as promptwith the line context embedded. - interaction
- Click any line number to copy
file:linereference; click finding's copy button to round-trip back as a prompt; toggle show nits to focus on critical/warning. - avoid
- Findings in a sidebar separated from the lines they discuss; color-only severity; runtime tokenizers / CDN syntax highlighters.
09 · table
Log stream — chronological event table
When events arrive in real time, not when you're auditing a static set. Different from the dense ops table: rows are chronological, levels carry urgency, expand-on-click reveals the structured payload, and the table itself is a scrollable container with a live-tail indicator.
- pick when
- The data is a tail of timestamped events with a categorical level; newest-first reading is the default; payloads have structure worth expanding.
- required
- Three live-state cues (pulsing dot + LIVE/PAUSED label + button mode); ochre flash on new rows (1.2s fade); FATAL fills its pill background (other levels outline-only); counts are against full data, not filtered; payload JSON syntax-colored with the diff's token palette.
- interaction
- Multi-select level chips; search across messages; click any row to expand the payload;
⏸ pause tailfreezes scroll position and state. - avoid
- Live-only state cue (state must read when video is muted or screenshot); identical pill styling for FATAL vs ERROR; expanding a row that destroys scroll position.
10 · chart
Scatter — two-dimension trade-off
For two genuinely independent dimensions across 10+ points, where the relationship is the signal — cost vs. quality, latency vs. throughput. Median crosshair guides split the cloud into quadrants ("cheap-and-good lives top-left"); marker shape carries identity alongside color so it survives greyscale. The discipline: if the points cluster on one axis, a scatter lies — rank with a bar instead.
- pick when
- Two independent, well-spread dimensions across ≥10 points; the trade-off / outliers / quadrants are the signal, not a ranking.
- required
- Both axes labeled with units + direction-of-good; log scale when a dimension spans >2 orders of magnitude; median crosshair guides with labeled medians; marker identity carried by shape AND color; hover/focus reveals name + both coords with a
<title>for assistive tech. - interaction
- Toggle always-on labels (off when dense — hover carries identity); toggle median guides; hover or keyboard-focus a point to pin a readout.
- avoid
- Scatter for ranking (that's a bar); always-on labels on a dense cloud (they collide — the omega-3 failure); an unscaled axis crushing a long-tailed dimension into the corner.
All ten primitives share the one-truth palette and the shape-chosen register split — several sit naturally inside instrument-register shapes (dashboard, comparison, developer, execution-log, runbook, triage-board, network-map), while others compose into reading or hybrid shapes when prose, slides, or transcripts need a real chart or table. Same tokens, same content-discipline rules.
No primitive matches that. to see all ten.