Design Tokens
Single source of visual truth for TrackItPilot's frontend. Tokens are plain CSS custom
properties in app/frontend/src/styles/tokens/
(colors.css, typography.css, spacing.css, effects.css, animation.css), imported
by app/frontend/src/styles.css. That same file also
defines a Tailwind v4 @theme block that maps Tailwind's utility scale onto the exact
same values, so hand-written component CSS (which references the --tip-*/semantic
vars directly) and Tailwind utility classes never drift apart. Change a token in one
place (tokens/*.css); update the mirrored @theme entry in styles.css if the value
changed.
Colors — scales
| Scale | Steps (light → dark / value) |
|---|---|
| Ink (neutral) | --tip-ink-50 #f9f9f9 · 100 #f4f4f5 · 150 #ececec · 200 #e4e4e7 · 300 #c2c2c6 · 400 #9a9aa2 · 500 #6a6a72 · 600 #52525b · 700 #38383a · 800 #26262a · 900 #171719 · --tip-white #fff |
| Emerald (positive/live) | --tip-emerald-50 #eafaf2 · 100 #c5f9e0 · 300 #7cc6a9 · 500 #00b573 · 700 #077451 |
| Gold / Amber (attention) | --tip-gold-500 #ffb400 · --tip-gold-600 #bd5814 · --tip-orange-500 #f97316 · --tip-amber-100 #fffdf4 · --tip-amber-200 #fdf3c1 · --tip-amber-border #f2e2ad |
| Red (critical) | --tip-red-100 #fde7ed · --tip-red-600 #d31f4a |
Colors — semantic aliases
Components should reference these, not raw scale steps, wherever a semantic meaning (page background, heading text, positive status) applies:
| Alias | Value | Alias | Value |
|---|---|---|---|
--surface-page |
--tip-ink-50 |
--text-heading |
--tip-ink-900 |
--surface-card |
--tip-white |
--text-body |
--tip-ink-800 |
--surface-card-alt |
--tip-amber-100 |
--text-muted |
--tip-ink-500 |
--surface-inverse |
--tip-ink-900 |
--text-inverse |
--tip-white |
--surface-hover |
--tip-ink-100 |
--text-on-inverse-muted |
--tip-ink-400 |
--border-hairline |
--tip-ink-150 |
--accent-primary |
--tip-emerald-500 |
--border-default |
--tip-ink-200 |
--accent-primary-strong |
--tip-emerald-700 |
--border-focus |
--tip-ink-900 |
Status semantics (drive tip-badge's tone and related accents): --status-positive-fg/bg
(emerald-700/100), --status-warning-fg/bg (gold-600/amber-200), --status-critical-fg/bg
(red-600/100), --status-neutral-fg/bg (ink-600/200). Tone (core/models/enums.ts) is
'positive' | 'warning' | 'critical' | 'neutral' | 'emerald' — emerald is a fifth,
brand-forward tone (--tip-emerald-100/700) distinct from positive's status alias.
Typography
| Token | Value | Token | Value |
|---|---|---|---|
--font-display |
Poppins (headings) | --font-ui |
Inter (body/controls) |
--font-mono |
JetBrains Mono (IDs, container numbers) | ||
--fs-display-xl |
34px (hero heading) | --fs-h3 |
15px |
--fs-display-lg |
28px (big stat numbers) | --fs-body |
14px (default UI) |
--fs-h1 |
22px (page title) | --fs-body-sm |
13px |
--fs-h2 |
18px (card heading) | --fs-label |
12px (table headers) |
--fs-micro |
11px (badges, captions) | ||
--fw-regular/medium/semibold/bold |
400/500/600/700 | --lh-tight/snug/normal |
1.1/1.3/1.5 |
--ls-label |
0.04em (uppercase labels) | --ls-tight |
-0.01em (display numbers) |
Spacing & radii
4px base grid: --space-1 (4px) through --space-16 (64px) — 1:4 2:8 3:12 4:16 5:20 6:24 8:32 10:40 12:48 16:64. Radii: --radius-sm 6px (chips) · --radius-md 10px
(buttons/inputs/controls) · --radius-lg 14px (cards) · --radius-xl 20px (menus, modals)
· --radius-pill 999px. Control heights: --control-h-sm 28px · --control-h-md 36px ·
--control-h-lg 44px.
Effects (shadows, focus ring)
--shadow-xs/sm/md/lg — increasingly soft/deep low-contrast black shadows (rgba(23,23,25,…)).
--shadow-hero — the dark hero/inverse-surface shadow. --ring-focus —
0 0 0 3px rgba(0,181,115,0.28) (emerald-tinted focus ring, used on inputs/menus/modal close).
--border-width — 1px.
Motion & reduced motion
@keyframes tip-skeleton-shimmer (loading shimmer, 1.4s ease infinite) and tip-spin live
in animation.css so they're available wherever styles.css is linked. Interactive
transitions are short and restrained: 0.12s ease for hover/focus/press (buttons scale to
0.97 on :active), 0.16s ease for menu/modal/toast enter animations. Every
component with an animation wraps it in @media (prefers-reduced-motion: reduce) to
disable it (see .tip-skeleton in animation.css, plus per-component overrides in modal,
toast, save-bar). styles.css also sets a global reduced-motion fallback
(animation-duration/transition-duration: 0.001ms !important).