Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 37 additions & 9 deletions docs/engineering/skills/chart-standards.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ This skill ensures consistent chart styling across PolicyEngine visualizations.
## Chart libraries

- **Recharts** (default) — used for all standard charts (bar, line, area, stacked bar)
- **Plotly** (exceptions only) — choropleth maps, hexagonal maps, waterfall charts, blog/notebook embedded JSON
- **Plotly** (exceptions only) — choropleth maps, hexagonal maps, waterfall charts, notebook embedded JSON, and legacy blog posts
- **Blog posts on policyengine.org** — ` ```chart ` blocks, rendered by the website with the ui-kit chart defaults (see [Blog post charts](#blog-post-charts))

## Recharts chart pattern

Expand Down Expand Up @@ -210,14 +211,14 @@ const description = (

These charts remain on Plotly (`react-plotly.js`):

| Chart | Reason |
| ------------------------------------- | ---------------------------------------------- |
| `USDistrictChoroplethMap.tsx` | No Recharts geo support |
| `HexagonalMap.tsx` | Custom scatter with hex markers |
| `BudgetaryImpactSubPage.tsx` | Waterfall chart (no native Recharts waterfall) |
| `BudgetaryImpactByProgramSubPage.tsx` | Waterfall chart |
| `MarkdownFormatter.tsx` | Blog posts embed native Plotly JSON |
| `NotebookRenderer.tsx` | Notebooks embed native Plotly JSON |
| Chart | Reason |
| --------------------------------------------------- | ------------------------------------------------------------------------ |
| `USDistrictChoroplethMap.tsx` | No Recharts geo support |
| `HexagonalMap.tsx` | Custom scatter with hex markers |
| `BudgetaryImpactSubPage.tsx` | Waterfall chart (no native Recharts waterfall) |
| `BudgetaryImpactByProgramSubPage.tsx` | Waterfall chart |
| `website/src/components/blog/MarkdownFormatter.tsx` | Legacy posts embed native Plotly JSON; new posts use ` ```chart ` blocks |
| `NotebookRenderer.tsx` | Notebooks embed native Plotly JSON |

For these files, continue using the Plotly patterns:

Expand All @@ -237,6 +238,33 @@ import {
/>;
```

## Blog post charts

Posts draw charts as ` ```chart ` fenced blocks holding a JSON spec.
`website/src/components/blog/BlogChart.tsx` parses each block and wraps it in a figure with
title, subtitle, watermark and source line. `bar` (in `BlogChart.tsx`) and `waterfall`
(`BlogWaterfallChart.tsx`) draw with Recharts, Inter, the `--chart-*` tokens and ui-kit's
shared axis, grid, tooltip and legend styles. `stateMap` (`BlogStateMap.tsx`) draws an 11 by
8 tile grid shaded from `--background` to `--primary`. Each file's header comment documents
its spec.

- Put data and labels in the spec, never styling. Pick series colors by token name:
`primary`, `secondary`, `tertiary`, `quaternary`, `quinary` or `negative`. `positive` maps
to `--success`, which the website does not define, so it falls back to the series palette.
- Do not add matplotlib PNGs or Plotly JSON to new posts. When a post needs a chart type the
website lacks (a line or scatter chart, say), add the type to `website/src/components/blog/`
with tests before the post uses it.
- `website/src/__tests__/components/blog-chart-posts.test.ts` fails CI when a post's chart
block does not parse. Keep the chart's numbers identical to the tables and text beside it.
- The legacy `app/src/components/blog/MarkdownFormatter.tsx` draws charts only from
` ```plotly ` blocks; a ` ```chart ` block shows there as a JSON code block labeled `chart`.
- The website builds Tailwind with the `tw:` prefix, so ui-kit components that rely on
unprefixed utility classes render unstyled there. Style blog chart parts inline with the
`:root` token variables (`--chart-1`, `--foreground`, `--muted-foreground`, `--border`,
`--font-sans`).
- For a newsletter or social image of a chart, screenshot the chart as the live post renders
it (headless Chromium at 2x) instead of re-plotting it.

## Anti-patterns

```tsx
Expand Down
Loading