Scaffold Chart Sins site with Astro + build-time Vega-Lite

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
This commit is contained in:
Claude
2026-08-05 11:57:52 +00:00
commit 2e17f8fa59
23 changed files with 978 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';
// Each "sin" is one Markdown/MDX file in src/content/sins/.
// Charts are authored as Vega-Lite JSON specs in src/charts/ and referenced
// here by their basename (without the .json extension). The page template
// renders `badChart` and `fixedChart` side by side automatically.
const sins = defineCollection({
loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/sins' }),
schema: z.object({
title: z.string(),
// Short one-line summary shown on the index gallery.
summary: z.string(),
// Grouping, e.g. "Misleading Scales", "Chart-Type Abuse".
category: z.string(),
// 1 (venial) … 5 (mortal). Drives the flame rating in the UI.
severity: z.number().int().min(1).max(5),
tags: z.array(z.string()).default([]),
// Basenames of Vega-Lite specs in src/charts/ (without .json).
badChart: z.string(),
fixedChart: z.string(),
// Optional publish date; unset entries sort last.
date: z.coerce.date().optional(),
draft: z.boolean().default(false),
}),
});
export const collections = { sins };