mirror of
https://github.com/olehomelchenko/chart-sins.git
synced 2026-08-08 02:22:43 +00:00
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:
@@ -0,0 +1,51 @@
|
||||
---
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import { REFERENCES, citeLabel, type RefKind } from '../lib/references';
|
||||
|
||||
const groups: { kind: RefKind; heading: string; blurb: string }[] = [
|
||||
{ kind: 'book', heading: 'The books', blurb: 'The shelf every chart argument can lean on.' },
|
||||
{ kind: 'paper', heading: 'The papers & essays', blurb: 'The primary sources — where the evidence actually lives.' },
|
||||
{ kind: 'site', heading: 'The sites', blurb: 'Living references you can link straight into a thread.' },
|
||||
];
|
||||
|
||||
const byKind = (kind: RefKind) => Object.values(REFERENCES).filter((r) => r.kind === kind);
|
||||
---
|
||||
|
||||
<BaseLayout title="The Canon — Chart Sins" description="The books, papers, and sites Chart Sins stands on.">
|
||||
<section class="wrap hero">
|
||||
<p class="hero__eyebrow">// the shoulders we stand on</p>
|
||||
<h1 class="expressive-display">The Canon</h1>
|
||||
<p class="hero__lede">
|
||||
Every sin here cites its sources. This is the whole shelf in one place —
|
||||
the giants of data visualization who already made the case, so you can
|
||||
link the receipt instead of relitigating it.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div class="wrap canon">
|
||||
{
|
||||
groups.map((g) => (
|
||||
<section class="canon__group">
|
||||
<h2 class="canon__heading productive-heading-03">{g.heading}</h2>
|
||||
<p class="helper">{g.blurb}</p>
|
||||
<ul class="canon__list">
|
||||
{byKind(g.kind).map((ref) => (
|
||||
<li class="canon__item">
|
||||
<div class="canon__cite">
|
||||
{ref.url ? (
|
||||
<a href={ref.url} rel="noopener" class="canon__work">{citeLabel(ref)}</a>
|
||||
) : (
|
||||
<span class="canon__work">{citeLabel(ref)}</span>
|
||||
)}
|
||||
{ref.work && <span class="canon__pub">{ref.work}</span>}
|
||||
{ref.free && <span class="receipt__free">free online</span>}
|
||||
</div>
|
||||
<p class="canon__blurb">{ref.blurb}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</BaseLayout>
|
||||
@@ -13,11 +13,13 @@ const sins = (await getCollection('sins', ({ data }) => !data.draft)).sort((a, b
|
||||
|
||||
<BaseLayout title="Chart Sins">
|
||||
<section class="wrap hero">
|
||||
<p class="hero__eyebrow">// a catalogue of data-visualization sins</p>
|
||||
<h1 class="expressive-display">Charts that lie,<br />and the honest fix.</h1>
|
||||
<p class="hero__eyebrow">// stop re-explaining the same chart mistake</p>
|
||||
<h1 class="expressive-display">Someone made a<br />misleading chart again.</h1>
|
||||
<p class="hero__lede">
|
||||
Each entry is a data-visualization mistake in the wild: the chart that
|
||||
misleads, why it fools the eye, and the honest version that repents.
|
||||
Don’t type the whole explanation for the hundredth time — send the link.
|
||||
Each sin shows the deceptive chart beside its honest twin, names the tell,
|
||||
and cites the giants who back you up. It’s <a href="https://nohello.net" rel="noopener">nohello.net</a>,
|
||||
for bad charts.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user