UI overhaul "Living Codex" A: tokens + fonts

- Replace the design tokens in globals.css with the Living Codex system (parchment
  light · candlelit dark, gilt accent), keeping every existing --app-*/utility name
  so nothing breaks. Adds surface-2/panel-2/line-strong/ink-soft/faint/accent-deep/
  accent-soft/accent-glow/verdigris + --app-accent-hue (one-number reskin) and
  paper-grain/gilt-rule/smallcaps helpers.
- Self-host the type system via @fontsource (offline-safe, no CDN/CSP change):
  Spectral (serif headings), Hanken Grotesk (UI), Spline Sans Mono (stats).
- Kept: dice/crit animations, print rule, reduced-motion, scrollbars.

223 unit + 34 e2e green; no console errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 20:53:24 +02:00
parent ea4522f877
commit 9244c91c23
4 changed files with 121 additions and 30 deletions
+100 -30
View File
@@ -3,56 +3,106 @@
/* Class-based dark mode: toggling `.dark` on <html> swaps the token values below. */
@custom-variant dark (&:where(.dark, .dark *));
/* ---- Semantic design tokens (light defaults) ---- */
/* ============================================================================
THE LIVING CODEX — design tokens. Parchment + gilt (light) · candlelit (dark).
Every --app-* name components already use is kept; values are swapped and a few
new tokens added. Retheme the entire accent by changing --app-accent-hue
(73 gilt · 38 ember · 12 oxblood · 285 arcane · 178 verdigris · 150 forest).
============================================================================ */
/* ---- Light: aged parchment ---- */
:root {
--app-surface: #f5f2ec; /* page background */
--app-panel: #fffdf9; /* cards / panels */
--app-elevated: #ece6da; /* raised controls */
--app-line: #d8cfbe; /* borders */
--app-ink: #232018; /* primary text */
--app-muted: #6b6457; /* secondary text */
--app-accent: #9a6b1f; /* brand (deep gold) */
--app-accent-soft: #c79a4a;
--app-accent-ink: #fffdf9; /* text on accent */
--app-danger: #b3261e;
--app-success: #2e7d32;
--app-warning: #b06f00;
--app-info: #1565c0;
--app-surface: oklch(0.945 0.018 84);
--app-surface-2: oklch(0.915 0.020 82); /* sunken wells */
--app-panel: oklch(0.972 0.013 88);
--app-panel-2: oklch(0.955 0.015 86);
--app-elevated: oklch(0.900 0.022 80);
--app-line: oklch(0.855 0.025 78);
--app-line-strong:oklch(0.760 0.030 76);
--app-ink: oklch(0.280 0.022 64);
--app-ink-soft: oklch(0.400 0.020 66); /* body copy */
--app-muted: oklch(0.520 0.020 70);
--app-faint: oklch(0.620 0.018 74); /* tertiary */
--app-accent: oklch(0.585 0.098 var(--app-accent-hue));
--app-accent-deep: oklch(0.500 0.090 calc(var(--app-accent-hue) - 5));
--app-accent-soft: oklch(0.680 0.110 calc(var(--app-accent-hue) + 5));
--app-accent-ink: oklch(0.985 0.010 88);
--app-accent-glow: oklch(0.585 0.098 var(--app-accent-hue) / 0.16);
--app-accent-hue: 73; /* change this ONE number to retheme the accent */
--app-verdigris: oklch(0.540 0.058 178);
--app-danger: oklch(0.520 0.150 32);
--app-danger-glow:oklch(0.520 0.150 32 / 0.16);
--app-success: oklch(0.560 0.085 150);
--app-warning: oklch(0.560 0.110 70);
--app-info: oklch(0.500 0.095 285);
--app-paper-mul: 1; /* set 0 to disable the paper grain */
--paper-url: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
color-scheme: light;
}
/* ---- Dark: candlelit ink ---- */
.dark {
--app-surface: #0f0f17;
--app-panel: #181826;
--app-elevated: #21212f;
--app-line: #2e2e40;
--app-ink: #e9e7ef;
--app-muted: #9a98ab;
--app-accent: #d4af37; /* gold */
--app-accent-soft: #b08d2e;
--app-accent-ink: #15151f;
--app-danger: #ef5350;
--app-success: #66bb6a;
--app-warning: #ffb74d;
--app-info: #64b5f6;
--app-surface: oklch(0.185 0.012 66);
--app-surface-2: oklch(0.150 0.012 66);
--app-panel: oklch(0.225 0.014 64);
--app-panel-2: oklch(0.255 0.015 62);
--app-elevated: oklch(0.290 0.016 60);
--app-line: oklch(0.330 0.018 58);
--app-line-strong:oklch(0.420 0.022 56);
--app-ink: oklch(0.925 0.016 82);
--app-ink-soft: oklch(0.820 0.016 80);
--app-muted: oklch(0.660 0.018 76);
--app-faint: oklch(0.520 0.018 72);
--app-accent: oklch(0.795 0.115 var(--app-accent-hue));
--app-accent-deep: oklch(0.700 0.105 calc(var(--app-accent-hue) - 4));
--app-accent-soft: oklch(0.860 0.120 calc(var(--app-accent-hue) + 3));
--app-accent-ink: oklch(0.180 0.020 60);
--app-accent-glow: oklch(0.795 0.115 var(--app-accent-hue) / 0.18);
--app-accent-hue: 82;
--app-verdigris: oklch(0.700 0.070 178);
--app-danger: oklch(0.640 0.155 36);
--app-danger-glow:oklch(0.640 0.155 36 / 0.20);
--app-success: oklch(0.700 0.095 152);
--app-warning: oklch(0.780 0.120 75);
--app-info: oklch(0.700 0.105 288);
color-scheme: dark;
}
/* Expose tokens as Tailwind color utilities: bg-surface, text-ink, border-line, etc. */
/* Expose tokens as Tailwind utilities: bg-surface, text-faint, border-line-strong, bg-accent-glow… */
@theme inline {
--color-surface: var(--app-surface);
--color-surface-2: var(--app-surface-2);
--color-panel: var(--app-panel);
--color-panel-2: var(--app-panel-2);
--color-elevated: var(--app-elevated);
--color-line: var(--app-line);
--color-line-strong: var(--app-line-strong);
--color-ink: var(--app-ink);
--color-ink-soft: var(--app-ink-soft);
--color-muted: var(--app-muted);
--color-faint: var(--app-faint);
--color-accent: var(--app-accent);
--color-accent-deep: var(--app-accent-deep);
--color-accent-soft: var(--app-accent-soft);
--color-accent-ink: var(--app-accent-ink);
--color-accent-glow: var(--app-accent-glow);
--color-verdigris: var(--app-verdigris);
--color-danger: var(--app-danger);
--color-danger-glow: var(--app-danger-glow);
--color-success: var(--app-success);
--color-warning: var(--app-warning);
--color-info: var(--app-info);
--font-display: 'Iowan Old Style', 'Palatino Linotype', 'Book Antiqua', Georgia, serif;
--font-display: 'Spectral', 'Iowan Old Style', 'Palatino Linotype', Georgia, serif;
--font-sans: 'Hanken Grotesk Variable', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
--font-mono: 'Spline Sans Mono Variable', ui-monospace, 'SF Mono', Menlo, monospace;
}
html,
@@ -65,10 +115,30 @@ body {
margin: 0;
background: var(--app-surface);
color: var(--app-ink);
font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
font-family: var(--font-sans);
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
/* ---- Pressed-paper grain. Put class="paper-grain" on any positioned panel. ---- */
.paper-grain { position: relative; }
.paper-grain::before {
content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
background-image: var(--paper-url);
background-size: 240px 240px;
opacity: calc(0.30 * var(--app-paper-mul));
}
.dark .paper-grain::before { opacity: calc(0.42 * var(--app-paper-mul)); }
.paper-grain > * { position: relative; z-index: 1; }
/* ---- Gilt hairline rule ---- */
.gilt-rule { height: 1px; border: 0; opacity: .5;
background: linear-gradient(90deg, transparent, var(--app-accent) 18%, var(--app-accent) 82%, transparent); }
/* ---- Small-caps section label ---- */
.smallcaps { font-family: var(--font-sans); text-transform: uppercase;
letter-spacing: .16em; font-size: 11px; font-weight: 600; color: var(--app-muted); }
/* Reduced-motion friendliness */
@media (prefers-reduced-motion: reduce) {
*,