Set up the static-site tech stack: Astro 5 with Markdown content collections, Vega-Lite specs rendered to static SVG at build time (zero client JS), plain scoped CSS, and a GitHub Pages deploy workflow. Includes three sample sins (truncated y-axis, dual-axis deception, pie chart overload), each with a bad and fixed chart spec, plus a gallery index and per-sin detail pages. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XPEvmMbac2fCvKXpQcovj8
3.4 KiB
Chart Sins
A catalogue of data-visualization sins — for each one: the chart that misleads, why it fools the eye, and the honest version that fixes it.
Built with Astro. Charts are authored as Vega-Lite specs and rendered to static SVG at build time, so pages ship with zero client-side JavaScript.
Tech stack
| Concern | Choice |
|---|---|
| Framework | Astro 5 (static output) |
| Content | Markdown/MDX via Astro Content Collections |
| Charts | Vega-Lite specs → SVG at build (headless Vega) |
| Styling | Plain scoped CSS + one global stylesheet |
| Hosting | GitHub Pages (via GitHub Actions) |
| Language | TypeScript |
Project layout
src/
├── content.config.ts # typed frontmatter schema for a "sin"
├── content/sins/ # one Markdown file per sin
├── charts/ # Vega-Lite JSON specs (bad + fixed)
├── components/VegaChart.astro # build-time Vega-Lite → SVG renderer
├── layouts/BaseLayout.astro
├── lib/charts.ts # spec lookup + severity helper
├── pages/
│ ├── index.astro # the gallery
│ └── sins/[...slug].astro # a sin's detail page
└── styles/global.css
Adding a new sin
-
Author two Vega-Lite specs in
src/charts/, e.g.my-sin-bad.jsonandmy-sin-fixed.json.Omit
$schema,config, and outerwidth/heighttuning you don't need — keep specs focused on the data and encoding. -
Create
src/content/sins/my-sin.mdwith frontmatter:--- title: "My Sin" summary: "One-line description shown on the gallery." category: "Misleading Scales" severity: 3 # 1 (venial) … 5 (mortal) tags: ["bar chart"] badChart: "my-sin-bad" # basename in src/charts/, no .json fixedChart: "my-sin-fixed" date: 2026-08-05 --- Prose explaining the sin, why it deceives, and the repentance. -
npm run devand check it. The typed schema will flag a mistyped field or a missing chart spec at build time.
Making a chart interactive (opt-in)
Charts are static SVG by default. To make one interactive, render it client-side
with vega-embed in a small Astro island (client:visible) on that page only,
instead of using <VegaChart>. This keeps the default fast while allowing
hover/zoom where a specific sin benefits from it.
Commands
| Command | Action |
|---|---|
npm install |
Install dependencies |
npm run dev |
Start the dev server at localhost:4321 |
npm run build |
Build the static site to ./dist |
npm run preview |
Preview the production build locally |
Deployment
Pushing to main triggers .github/workflows/deploy.yml, which builds the site
and publishes it to GitHub Pages. Enable it once under
Settings → Pages → Build and deployment → Source: GitHub Actions.
The site is configured for the project path https://olehomelchenko.github.io/chart-sins
(site + base in astro.config.mjs). If you move to a custom domain, set
site to it and remove base.