Remove internal build decisions from visitor-facing content

The public site (and DOM) shouldn't advertise our tooling choices:

- Drop the design-system credit and typeface note from the footer; replace
  with visitor-useful copy ("Built to be sent" / "The canon")
- Remove the external-inspiration reference from the homepage lede
- Neutralize the meta description
- Rename the `cds-` CSS class prefix to `site-`
- Scrub internal names from source comments and the chart theme variable

Functional font-family references are kept; only the advertising is removed.

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:40:59 +00:00
parent e0d3f9f773
commit f010ec6a3c
5 changed files with 67 additions and 72 deletions
+11 -11
View File
@@ -1,13 +1,13 @@
---
// Build-time Vega-Lite renderer, themed on Carbon.
// Build-time Vega-Lite renderer.
//
// Runs only during `astro build` (and dev SSR), never in the browser: it
// compiles a Vega-Lite spec to Vega with a Carbon theme (IBM Plex type +
// Carbon's data-visualization palette), renders it headlessly to a static SVG
// string, and inlines it. Zero client-side JavaScript, no Vega runtime shipped.
// compiles a Vega-Lite spec to Vega with the site theme, renders it headlessly
// to a static SVG string, and inlines it. Zero client-side JavaScript, no Vega
// runtime shipped.
//
// The chart sits on a fixed light Carbon canvas (--chart-canvas) in both
// themes, so this single baked render keeps AA contrast in light and dark.
// The chart sits on a fixed light canvas (--chart-canvas) in both themes, so
// this single baked render keeps AA contrast in light and dark.
import * as vega from 'vega';
import { compile, type TopLevelSpec, type Config } from 'vega-lite';
@@ -19,8 +19,8 @@ interface Props {
const { spec, title, caption } = Astro.props;
// Carbon Vega theme — the design-system parameters the renderer consumes.
const carbonTheme: Config = {
// Chart theme — the visual parameters the renderer consumes.
const chartTheme: Config = {
font: 'IBM Plex Sans',
background: 'transparent',
view: { stroke: null },
@@ -54,8 +54,8 @@ const carbonTheme: Config = {
titleColor: '#393939',
symbolType: 'square',
},
// Carbon data-visualization categorical palette (validated: passes the
// dataviz colorblind/contrast checks on the --chart-canvas surface).
// Categorical palette (validated colorblind-safe/contrast on the
// --chart-canvas surface).
range: {
category: ['#8a3ffc', '#009d9a', '#fa4d56', '#0f62fe', '#24a148', '#d02670', '#b28600', '#1192e8'],
},
@@ -65,7 +65,7 @@ let svg = '';
let error: string | null = null;
try {
const vgSpec = compile(spec, { config: carbonTheme }).spec;
const vgSpec = compile(spec, { config: chartTheme }).spec;
const view = new vega.View(vega.parse(vgSpec), { renderer: 'none' }).initialize();
svg = await view.toSVG();
view.finalize();
+15 -18
View File
@@ -1,5 +1,5 @@
---
// IBM Plex, self-hosted via @fontsource (no external requests) — Carbon's typeface.
// Self-hosted webfonts via @fontsource (no external requests).
import '@fontsource/ibm-plex-sans/300.css';
import '@fontsource/ibm-plex-sans/400.css';
import '@fontsource/ibm-plex-sans/600.css';
@@ -12,7 +12,7 @@ interface Props {
description?: string;
}
const { title, description = 'A catalogue of data-visualization sins, styled on the Carbon Design System.' } =
const { title, description = 'A catalogue of data-visualization sins — the deceptive chart, why it misleads, and the honest fix.' } =
Astro.props;
---
@@ -27,12 +27,12 @@ const { title, description = 'A catalogue of data-visualization sins, styled on
<body>
<a class="skip-link" href="#main">Skip to content</a>
<header class="cds-header">
<div class="cds-header__inner">
<a class="cds-header__name" href={href()}>
<header class="site-header">
<div class="site-header__inner">
<a class="site-header__name" href={href()}>
Chart&nbsp;Sins <span class="mono">/ honest data-viz</span>
</a>
<nav class="cds-header__nav" aria-label="Primary">
<nav class="site-header__nav" aria-label="Primary">
<a href={href()}>Sins</a>
<a href={href('canon/')}>Canon</a>
</nav>
@@ -43,22 +43,19 @@ const { title, description = 'A catalogue of data-visualization sins, styled on
<slot />
</main>
<footer class="cds-footer">
<div class="wrap cds-footer__inner">
<div class="cds-footer__col">
<footer class="site-footer">
<div class="wrap site-footer__inner">
<div class="site-footer__col">
<strong>Chart Sins</strong>
<p>An educational catalogue of data-visualization mistakes — the bad chart, why it deceives, and the honest fix.</p>
</div>
<div class="cds-footer__col">
<strong>Design</strong>
<p>
Built on the <a href="https://carbondesignsystem.com" rel="noopener">Carbon Design System</a>'s
principles — open, modular, consistent, inclusive. Typeface: IBM&nbsp;Plex.
</p>
<div class="site-footer__col">
<strong>Built to be sent</strong>
<p>Every sin has a permanent link and cites its sources — made to share, not to re-explain.</p>
</div>
<div class="cds-footer__col">
<strong>Charts</strong>
<p>Authored in Vega-Lite, rendered to static SVG at build time on Carbon's data-visualization palette.</p>
<div class="site-footer__col">
<strong>The canon</strong>
<p>Each entry stands on the shoulders of the field's giants. <a href={href('canon/')}>See the sources →</a></p>
</div>
</div>
</footer>
+3 -3
View File
@@ -23,12 +23,12 @@ export function getChartSpec(name: string): TopLevelSpec {
return spec;
}
/** A 15 severity as five booleans, for the Carbon-style severity meter. */
/** A 15 severity as five booleans, for the severity meter. */
export function severityUnits(severity: number): boolean[] {
return Array.from({ length: 5 }, (_, i) => i < severity);
}
// Stable category -> Carbon tag color. Known categories get a hand-picked hue;
// Stable category -> tag color. Known categories get a hand-picked hue;
// anything new hashes to a stable colour so the palette never churns.
const TAG_COLORS = ['purple', 'teal', 'red', 'blue', 'magenta'] as const;
const CATEGORY_TAG: Record<string, (typeof TAG_COLORS)[number]> = {
@@ -37,7 +37,7 @@ const CATEGORY_TAG: Record<string, (typeof TAG_COLORS)[number]> = {
'Chart-Type Abuse': 'purple',
};
/** Map a category label to a Carbon tag colour modifier. */
/** Map a category label to a tag colour modifier. */
export function tagModifier(category: string): (typeof TAG_COLORS)[number] {
if (CATEGORY_TAG[category]) return CATEGORY_TAG[category];
let hash = 0;
+1 -2
View File
@@ -18,8 +18,7 @@ const sins = (await getCollection('sins', ({ data }) => !data.draft)).sort((a, b
<p class="hero__lede">
Dont 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. Its <a href="https://nohello.net" rel="noopener">nohello.net</a>,
for bad charts.
and cites the giants who back you up.
</p>
</section>
+37 -38
View File
@@ -1,21 +1,20 @@
/*
Chart Sins — styled on the Carbon Design System.
Chart Sins — token-driven style layer.
This is a token-driven layer, the way Carbon itself is built:
- Color/theme tokens mirror Carbon's White + Gray 100 themes, layered
(background -> layer-01 -> layer-02) rather than picked ad hoc.
- Spacing follows Carbon's 8px mini-unit scale ($spacing-01..13).
- Type is IBM Plex, split into Carbon's "productive" (UI/reading) and
"expressive" (large, light-weight display) moments.
- Motion uses Carbon's productive easing + duration tokens.
- Geometry is Carbon: square corners, 1px subtle borders, a 2px focus ring.
- Color/theme tokens are layered (background -> layer-01 -> layer-02) with
a light and a dark theme.
- Spacing follows an 8px mini-unit scale.
- Type is split into "productive" (UI/reading) and "expressive" (large,
light-weight display) roles.
- Motion uses shared easing + duration tokens.
- Geometry: square corners, 1px subtle borders, a 2px focus ring.
*/
/* ------------------------------------------------------------------ *\
Tokens — White theme (light) is the default :root.
\* ------------------------------------------------------------------ */
:root {
/* --- spacing scale (Carbon $spacing-01 .. 13) --- */
/* --- spacing scale (8px mini-unit scale) --- */
--spacing-01: 0.125rem;
--spacing-02: 0.25rem;
--spacing-03: 0.5rem;
@@ -30,7 +29,7 @@
--spacing-12: 6rem;
--spacing-13: 10rem;
/* --- motion (Carbon) --- */
/* --- motion --- */
--ease-productive: cubic-bezier(0.2, 0, 0.38, 0.9);
--ease-expressive: cubic-bezier(0.4, 0.14, 0.3, 1);
--dur-fast: 110ms;
@@ -39,7 +38,7 @@
/* --- layout --- */
--page-max: 82rem;
--measure: 40rem; /* Carbon reading measure ~640px */
--measure: 40rem; /* reading measure ~640px */
/* --- color tokens: White theme --- */
--background: #ffffff;
@@ -59,13 +58,13 @@
--support-error: #da1e28;
--support-success: #198038;
/* header/footer shell is always dark in Carbon */
/* header/footer shell is always dark in both themes */
--shell-bg: #161616;
--shell-border: #393939;
--shell-text: #f4f4f4;
--shell-text-secondary: #a8a8a8;
/* chart canvas — a fixed light Carbon layer in BOTH themes, so the
/* chart canvas — a fixed light layer in BOTH themes, so the
build-time-baked chart ink keeps AA contrast without theme-reactive SVG */
--chart-canvas: #f4f4f4;
@@ -115,7 +114,7 @@ html {
body {
margin: 0;
/* Carbon productive body-02 */
/* base body text */
font-size: 1rem;
line-height: 1.5;
letter-spacing: 0;
@@ -133,7 +132,7 @@ a:hover {
border-bottom-color: currentColor;
}
/* Carbon focus ring — 2px, high-contrast, square */
/* focus ring — 2px, high-contrast, square */
:where(a, button, [tabindex]):focus-visible {
outline: 2px solid var(--focus);
outline-offset: 2px;
@@ -149,10 +148,10 @@ code {
}
/* ------------------------------------------------------------------ *\
Type scale — productive + expressive (Carbon)
Type scale — productive + expressive
\* ------------------------------------------------------------------ */
.expressive-display {
font-weight: 300; /* Carbon expressive uses light weight at large sizes */
font-weight: 300; /* expressive uses light weight at large sizes */
font-size: clamp(2.25rem, 1.4rem + 4.2vw, 4rem);
line-height: 1.07;
letter-spacing: -0.02em;
@@ -196,16 +195,16 @@ code {
}
/* ------------------------------------------------------------------ *\
UI Shell header — Carbon's header is dark in every theme
Header — dark in every theme
\* ------------------------------------------------------------------ */
.cds-header {
.site-header {
position: sticky;
top: 0;
z-index: 10;
background: var(--shell-bg);
border-bottom: 1px solid var(--shell-border);
}
.cds-header__inner {
.site-header__inner {
display: flex;
align-items: center;
gap: var(--spacing-05);
@@ -214,7 +213,7 @@ code {
margin-inline: auto;
padding-inline: var(--spacing-05);
}
.cds-header__name {
.site-header__name {
color: var(--shell-text);
font-weight: 600;
font-size: 1rem;
@@ -222,37 +221,37 @@ code {
text-decoration: none;
border: 0;
}
.cds-header__name:hover {
.site-header__name:hover {
color: var(--shell-text);
}
.cds-header__name .mono {
.site-header__name .mono {
font-family: var(--font-mono);
font-weight: 400;
}
.cds-header__tag {
.site-header__tag {
color: var(--shell-text-secondary);
font-size: 0.8125rem;
font-family: var(--font-mono);
letter-spacing: 0;
}
.cds-header__nav {
.site-header__nav {
margin-left: auto;
display: flex;
gap: var(--spacing-06);
}
.cds-header__nav a {
.site-header__nav a {
color: var(--shell-text-secondary);
font-size: 0.875rem;
text-decoration: none;
border: 0;
padding-block: var(--spacing-02);
}
.cds-header__nav a:hover {
.site-header__nav a:hover {
color: var(--shell-text);
}
/* ------------------------------------------------------------------ *\
Tag (Carbon tags are the one rounded element)
Tag (the one rounded element)
\* ------------------------------------------------------------------ */
.tag {
display: inline-flex;
@@ -268,7 +267,7 @@ code {
color: var(--text-secondary);
border: 1px solid transparent;
}
/* Carbon tag color pairings (light bg + readable ink) */
/* tag color pairings (light bg + readable ink) */
.tag--purple { background: #e8daff; color: #491d8b; }
.tag--teal { background: #9ef0f0; color: #005d5d; }
.tag--red { background: #ffd7d9; color: #a2191f; }
@@ -283,7 +282,7 @@ code {
}
/* ------------------------------------------------------------------ *\
Severity meter — 5 geometric units (Carbon-flavored, not emoji)
Severity meter — 5 geometric units (not emoji)
\* ------------------------------------------------------------------ */
.severity {
display: inline-flex;
@@ -335,7 +334,7 @@ code {
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
gap: 1px; /* Carbon tiles sit in a 1px grid of border color */
gap: 1px; /* tiles sit in a 1px grid of border color */
background: var(--border-subtle);
border: 1px solid var(--border-subtle);
margin-block: var(--spacing-09) var(--spacing-10);
@@ -475,7 +474,7 @@ code {
background: var(--support-success);
}
/* build-time chart, on a fixed light Carbon canvas */
/* build-time chart, on a fixed light canvas */
.vega-chart {
margin: 0;
}
@@ -511,7 +510,7 @@ code {
padding: 0;
}
/* long-form prose — Carbon reading measure + productive type */
/* long-form prose — reading measure + productive type */
.prose {
max-width: var(--measure);
margin-top: var(--spacing-08);
@@ -727,12 +726,12 @@ a.canon__work {
/* ------------------------------------------------------------------ *\
Footer + colophon
\* ------------------------------------------------------------------ */
.cds-footer {
.site-footer {
background: var(--layer-01);
border-top: 1px solid var(--border-subtle);
margin-top: var(--spacing-12);
}
.cds-footer__inner {
.site-footer__inner {
display: flex;
flex-wrap: wrap;
gap: var(--spacing-05) var(--spacing-09);
@@ -740,13 +739,13 @@ a.canon__work {
font-size: 0.8125rem;
color: var(--text-secondary);
}
.cds-footer__col strong {
.site-footer__col strong {
display: block;
color: var(--text-primary);
font-weight: 600;
margin-bottom: var(--spacing-03);
}
.cds-footer__col p {
.site-footer__col p {
margin: 0;
max-width: 22rem;
line-height: 1.5;