feat(ui): design-system foundation — shared token layer + fonts

Establish the visual-redesign foundation (task group 1): a single
shared source of design tokens both apps consume, so Planner and
Journal read from one palette/typography layer instead of drifting
per-app.

- New @trails-cool/ui package exporting theme.css: Tailwind v4 @theme
  tokens (warm off-white surfaces, one sage accent, earthy overnight
  tones, elevation-gradient colors, shadows) extracted from the
  visual-redesign mockup.
- Self-hosted Outfit (body) + Geist Mono (stats) via @fontsource-variable
  — privacy-first, no Google Fonts CDN.
- Both apps import the theme and set the base canvas to the warm
  off-white token + Outfit as the default font.

Foundation only: tokens are now available as Tailwind utilities
(bg-bg-raised, text-accent, font-mono, …) and raw CSS vars. Wiring
them into specific surfaces (topbar, sidebar, markers, elevation
chart) is later task groups.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-07-15 23:00:28 +02:00
parent ad9a633ae0
commit 34c3a31e73
No known key found for this signature in database
GPG key ID: A32FF691A0F752D9
11 changed files with 126 additions and 6 deletions

71
packages/ui/src/theme.css Normal file
View file

@ -0,0 +1,71 @@
/*
* trails.cool design tokens the single source of truth for both apps.
*
* Direction: "Trail Worn warmth + Nordic Precision lightness" warm
* off-whites, one muted sage-forest accent, earthy overnight tones, Outfit
* for text and Geist Mono for stats. Values come from the design exploration
* in openspec/changes/visual-redesign/mockup.html.
*
* Consume the tokens as Tailwind utilities (bg-bg-raised, text-text-md,
* text-accent, shadow-sm, font-mono, ) or as raw CSS vars (var(--color-accent),
* var(--color-eg-lo), ) where a utility can't reach e.g. canvas/SVG fills.
*
* Fonts are self-hosted (privacy-first no Google Fonts CDN).
*/
@import "@fontsource-variable/outfit";
@import "@fontsource-variable/geist-mono";
@theme {
/* ── Surface ── */
--color-bg: #f5f2eb; /* warm off-white — like uncoated paper */
--color-bg-subtle: #edeae1; /* slightly deeper — card backgrounds */
--color-bg-raised: #faf8f4; /* topbar, sidebar, elevated surfaces */
--color-map-tint: #e4dfd2; /* map background tone */
/* ── Text ── */
--color-text-hi: #1a1916; /* near-black — primary */
--color-text-md: #5c5847; /* secondary labels */
--color-text-lo: #9a9484; /* tertiary, placeholders */
--color-text-inv: #faf8f4; /* on dark surfaces */
/* ── Accent: one muted sage-forest green ── */
--color-accent: #4a6b40; /* primary accent */
--color-accent-dim: #6a8b5e; /* softer variant */
--color-accent-bg: rgba(74, 107, 64, 0.08);
--color-accent-border: rgba(74, 107, 64, 0.2);
/* ── Overnight / stop: warm amber-brown, not orange ── */
--color-stop: #8b6d3a;
--color-stop-bg: rgba(139, 109, 58, 0.1);
--color-stop-border: rgba(139, 109, 58, 0.25);
/* ── Danger / no-go ── */
--color-nogo: rgba(160, 60, 60, 0.12);
--color-nogo-border: rgba(160, 60, 60, 0.3);
/* ── UI chrome ── */
--color-border: #ddd9d0;
--color-border-md: #cac6bc;
/* ── Elevation gradient (route line + chart) ── */
--color-eg-lo: #5a8f46;
--color-eg-mid: #c4a840;
--color-eg-hi: #c46040;
/* ── Shadows ── */
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08), 0 1px 4px rgba(0, 0, 0, 0.05);
/* ── Typography ── */
--font-sans: "Outfit Variable", "Outfit", ui-sans-serif, system-ui, sans-serif;
--font-mono: "Geist Mono Variable", ui-monospace, "SF Mono", monospace;
}
@layer base {
body {
background-color: var(--color-bg);
color: var(--color-text-hi);
font-family: var(--font-sans);
}
}