mirror of
https://github.com/olehomelchenko/chart-sins.git
synced 2026-08-08 02:22:43 +00:00
Generalize pokes; drop citations from OG card
- Rewrite pokes to name the sin itself, not our sample's exact numbers (a visitor didn't "start at 90" — they mis-set the baseline) - Remove the "cited:" line from the OG card; citations stay on the page Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XPEvmMbac2fCvKXpQcovj8
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: "The Dual-Axis Deception"
|
||||
summary: "Two unrelated series on two independent axes, engineered to look correlated."
|
||||
poke: "Two y-axes, tuned until the lines kiss. Congratulations — you can now make anything 'cause' anything."
|
||||
poke: "Two y-axes, scaled until the lines overlap — and suddenly anything 'causes' anything."
|
||||
category: "False Relationships"
|
||||
severity: 5
|
||||
tags: ["dual axis", "correlation", "line chart"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: "Pie Chart Overload"
|
||||
summary: "Eight near-identical slices no human eye can rank. A bar chart was right there."
|
||||
poke: "Eight slices, three of them the same size. Nobody can read this. A bar chart could."
|
||||
poke: "More slices than anyone can rank by eye. A bar chart was right there."
|
||||
category: "Chart-Type Abuse"
|
||||
severity: 3
|
||||
tags: ["pie chart", "part-to-whole", "comparison"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: "The Truncated Y-Axis"
|
||||
summary: "Chopping the baseline off a bar chart to make small differences look enormous."
|
||||
poke: "You started the y-axis at 90. That's not a bar chart — it's a magic trick."
|
||||
poke: "Your bars don't start at zero. That's not a bar chart — it's a magic trick."
|
||||
category: "Misleading Scales"
|
||||
severity: 4
|
||||
tags: ["bar chart", "axis", "exaggeration"]
|
||||
|
||||
+1
-14
@@ -5,7 +5,6 @@ import { html } from 'satori-html';
|
||||
import { Resvg } from '@resvg/resvg-js';
|
||||
import type { TopLevelSpec } from 'vega-lite';
|
||||
import { renderChartSvg } from './renderChart';
|
||||
import { getReference } from './references';
|
||||
|
||||
// OpenGraph card generator. Runs only at build time. Composes a 1200×630 card
|
||||
// with satori (text → vector paths, so the raster step needs no fonts) and
|
||||
@@ -34,13 +33,6 @@ const fonts = [
|
||||
const esc = (s: string) =>
|
||||
s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
|
||||
/** Surname of the first author, for a compact "cited: Tufte, Cairo" line. */
|
||||
function shortAuthor(key: string): string {
|
||||
const first = getReference(key).authors.split(/ & |,|;/)[0].trim();
|
||||
const parts = first.split(' ');
|
||||
return parts[parts.length - 1];
|
||||
}
|
||||
|
||||
/** Render a chart spec to a PNG data URI at a fixed display height (2× for crispness).
|
||||
* Fixing height (not width) keeps tall vconcat charts from overflowing the card. */
|
||||
async function chartImage(spec: TopLevelSpec, displayHeight: number) {
|
||||
@@ -79,13 +71,11 @@ export interface SinCard {
|
||||
severity: number;
|
||||
badSpec: TopLevelSpec;
|
||||
fixedSpec: TopLevelSpec;
|
||||
citationKeys: string[];
|
||||
}
|
||||
|
||||
export async function renderSinCard(d: SinCard): Promise<Buffer> {
|
||||
const bad = await chartImage(d.badSpec, 250);
|
||||
const fixed = await chartImage(d.fixedSpec, 250);
|
||||
const cited = d.citationKeys.slice(0, 3).map(shortAuthor).join(', ');
|
||||
|
||||
const panel = (label: string, color: string, img: { uri: string; width: number; height: number }) => `
|
||||
<div style="display:flex;flex-direction:column;background:#f4f4f4;padding:12px;border-top:5px solid ${color}">
|
||||
@@ -104,10 +94,7 @@ export async function renderSinCard(d: SinCard): Promise<Buffer> {
|
||||
${panel('THE SIN', '#fa4d56', bad)}
|
||||
${panel('THE FIX', '#42be65', fixed)}
|
||||
</div>
|
||||
<div style="display:flex;justify-content:space-between;margin-top:22px;font-size:17px;color:#a8a8a8">
|
||||
<div style="display:flex">${esc(d.category)}</div>
|
||||
<div style="display:flex">${cited ? 'cited: ' + esc(cited) : ''}</div>
|
||||
</div>
|
||||
<div style="display:flex;margin-top:22px;font-size:17px;color:#a8a8a8">${esc(d.category)}</div>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ export const GET: APIRoute = async ({ props }) => {
|
||||
severity: sin.data.severity,
|
||||
badSpec: getChartSpec(sin.data.badChart),
|
||||
fixedSpec: getChartSpec(sin.data.fixedChart),
|
||||
citationKeys: sin.data.citations.map((c) => c.key),
|
||||
});
|
||||
return new Response(new Uint8Array(png), {
|
||||
headers: { 'Content-Type': 'image/png', 'Cache-Control': 'public, max-age=31536000, immutable' },
|
||||
|
||||
Reference in New Issue
Block a user