Claude 53e9803a61 Give every sin a pictogram for navigation
The gallery and the cross-links were all text, so nothing on either told
you what the chart problem actually looked like.

Shrinking the bad chart doesn't work here: in nearly every sin the tell
lives in the axis, the scale or the legend, which is exactly what a chart
loses at thumbnail size. Eight miniatures would have been eight
indistinguishable squiggles with the evidence stripped out.

So each sin gets a hand-drawn glyph of the tell instead — a cut baseline,
a down-pointing axis arrow, class breaks that moved between two ramps.
Banner across the top of the gallery tile, small glyph beside each
"Often found together" link. Drawn against currentColor plus three theme
tokens, so unlike the baked chart SVGs these follow light and dark.

Missing thumbnail throws at build, same as an unknown citation key.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ULE5RRxdQE1ebwefEd2eCM
2026-08-06 10:31:13 +00:00
2026-08-05 12:41:00 +00:00

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 and styled on the Carbon Design System. 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)
Design Carbon Design System — tokens, type scale, spacing, motion, themes
Typeface IBM Plex Sans / Mono (self-hosted via @fontsource)
Styling Token-driven CSS (src/styles/global.css)
Hosting GitHub Pages (via GitHub Actions)
Language TypeScript

On the Carbon adoption

Carbon ships React/web-component libraries built for application UIs and they ship JavaScript; this is a static content site, so instead of the component runtime we adopt Carbon at the design-token level — its color tokens and White/Gray-100 themes, the 8px spacing scale, the productive/expressive type distinction (IBM Plex), motion tokens, square geometry, the focus ring, and Carbon's data-visualization palette for the charts (validated for colorblind-safety and contrast on the chart surface).

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

  1. Author two Vega-Lite specs in src/charts/, e.g. my-sin-bad.json and my-sin-fixed.json.

    Omit $schema, config, and outer width/height tuning you don't need — keep specs focused on the data and encoding.

  2. Create src/content/sins/my-sin.md with 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.
    
  3. npm run dev and 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.

S
Description
website to shame you for your chart sins
Readme 676 KiB
Languages
TypeScript 37.1%
CSS 34%
Astro 28.3%
JavaScript 0.6%