Reframe as a linkable, cited callout ("nohello for charts")

Make each sin a self-contained page you send instead of re-explaining:

- Per-sin "poke" one-liner (the shareable callout)
- Citations system: a canonical bibliography (src/lib/references.ts) of the
  field's giants — Munzner, Knaflic, Tufte, Cairo, Few, Cleveland & McGill,
  Wilke, Healy, Calling Bullshit, data-to-viz, Junk Charts, Spurious
  Correlations, FT Visual Vocabulary — cited per sin with a relevance note
- "Don't take our word for it" receipts block on each sin
- /canon page: the full annotated bibliography
- Share control: copy-link "send them the receipt" (progressive enhancement)
- Restructured sin page: poke -> proof -> why -> receipts -> share
- Header nav (Sins / Canon); homepage reframed to the "send the link" mission

Also fix the truncated-axis bad chart: clamp the [90,100] domain so the bar
baseline doesn't overflow thousands of pixels off-canvas.

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 13:21:07 +00:00
parent a3ab9f8a79
commit e0d3f9f773
13 changed files with 578 additions and 11 deletions
+16 -1
View File
@@ -2,6 +2,8 @@
import { getCollection, render } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro';
import VegaChart from '../../components/VegaChart.astro';
import Citations from '../../components/Citations.astro';
import ShareLink from '../../components/ShareLink.astro';
import { getChartSpec, severityUnits, tagModifier } from '../../lib/charts';
import { href } from '../../lib/url';
@@ -18,9 +20,12 @@ const { Content } = await render(sin);
const badSpec = getChartSpec(sin.data.badChart);
const fixedSpec = getChartSpec(sin.data.fixedChart);
// Canonical, shareable URL for this sin — the thing you paste into a thread.
const pageUrl = new URL(href(`sins/${sin.id}/`), Astro.site).toString();
---
<BaseLayout title={`${sin.data.title} — Chart Sins`} description={sin.data.summary}>
<BaseLayout title={`${sin.data.title} — Chart Sins`} description={sin.data.poke}>
<article class="wrap sin">
<nav class="breadcrumb"><a href={href()}>All sins</a> / {sin.data.title}</nav>
@@ -37,6 +42,7 @@ const fixedSpec = getChartSpec(sin.data.fixedChart);
</span>
</div>
<h1 class="sin__title expressive-heading">{sin.data.title}</h1>
<p class="sin__poke">{sin.data.poke}</p>
<div class="sin__tags">
{sin.data.tags.map((t) => <span class="tag">{t}</span>)}
</div>
@@ -52,9 +58,18 @@ const fixedSpec = getChartSpec(sin.data.fixedChart);
<VegaChart spec={fixedSpec} title={`Honest chart: ${sin.data.title}`} />
</div>
</div>
<p class="compare__note">Same numbers, both charts. Only the design changed.</p>
<div class="prose">
<Content />
</div>
<Citations citations={sin.data.citations} />
<ShareLink url={pageUrl} />
<p class="more-canon">
Building a case? See the <a href={href('canon/')}>full canon →</a>
</p>
</article>
</BaseLayout>