Files
electric-cherry/src/lib/ui/styles/tokens.css
T

271 lines
8.8 KiB
CSS

/* Electric Cherry — design tokens.
This file is the single source of truth for color, type, spacing, motion,
and elevation. It's organized in three layers:
1. RAW RAMPS — the actual color/type/space values. Never referenced
directly by components.
2. SEMANTIC ALIASES — what components should reference (--surface,
--text-primary, --state-online, --space-4, etc.). Swapping the palette
or spacing rhythm later is a single-file edit.
3. LEGACY ALIASES — old names (--bg-page, --accent, --ink-300, --pad-card…)
mapped onto new semantic values. Kept alive so existing components render
during the phased redesign; each page rewrite removes its usage, and
this block shrinks over time.
Design principles this file encodes:
· One accent (blue). Cyan/amber/red/green mean state only.
· Depth by light, not lines. One ambient shadow + subtle translucency.
· One typeface (Inter). Mono for data. Display face lives inside BrandMark.
· Motion tokens standardize easings + durations across the product. */
:root {
/* ---------- 1. RAW RAMPS ---------- */
/* Blue — one hue, five stops. Brand primary + focus/link. */
--blue-950: #060b16;
--blue-900: #0d1826;
--blue-700: #1c3d6b;
--blue-500: #4da3e6;
--blue-300: #7cc0f5;
/* Neutral gray — cool-tinted, distinct from the blue ramp so surfaces
stop being tinted-blue on tinted-blue. */
--gray-975: #08090c;
--gray-950: #0d0f14;
--gray-925: #12151c;
--gray-900: #171b24;
--gray-850: #1f242f;
--gray-800: #262c39;
--gray-700: #363d4d;
--gray-500: #6b7488;
--gray-400: #8892a5;
--gray-300: #a8b2c4;
--gray-200: #c5cddc;
--gray-100: #dfe4ee;
--gray-050: #eef2f8;
/* State — reserved for actual state, never decoration. */
--state-online: #7cd4ff;
--state-warning: #ffb020;
--state-danger: #ff5670;
--state-restarting: #7cc0f5;
/* ---------- 2. SEMANTIC ALIASES ---------- */
/* Surfaces — three tiers of depth, all near-black cool gray. */
--surface-sunken: var(--gray-975);
--surface: var(--gray-925);
--surface-raised: var(--gray-850);
--surface-overlay: var(--gray-800);
/* Text — four steps of hierarchy on dark. */
--text-primary: var(--gray-050);
--text-secondary: var(--gray-300);
--text-tertiary: #98a4b8;
--text-quaternary: var(--gray-400);
--text-on-accent: #08111f;
/* Border — hairlines, no more. */
--border-subtle: rgba(255, 255, 255, 0.06);
--border-strong: rgba(255, 255, 255, 0.12);
--border-focus: var(--blue-500);
--border-control: #576176;
--border-control-hover: #78869f;
/* Accent — one color, one voice. */
--accent-primary: var(--blue-500);
--accent-primary-hover: var(--blue-300);
--accent-primary-active: #398fcf;
/* Metrics: unknown uses a pattern as well as color. */
--chart-active: var(--accent-primary);
--chart-idle: #7b9baf;
--chart-unknown-base: #605b64;
--chart-unknown-stripe: #a7a0ad;
/* Typography — Inter everywhere, mono for data.
Display face lives inside BrandMark; no global --font-display token. */
--font-body: 'Inter', system-ui, -apple-system, sans-serif;
--font-numeric: 'Inter', system-ui, -apple-system, sans-serif;
--font-mono: 'JetBrains Mono', ui-monospace, monospace;
/* Type scale — cleaner ramp anchored at 15px body. */
--text-xs: 12px;
--text-sm: 13px;
--text-base: 15px;
--text-md: 17px;
--text-lg: 20px;
--text-xl: 24px;
--text-2xl: 32px;
--text-3xl: 44px;
--leading-tight: 1.15;
--leading-body: 1.55;
--leading-mono: 1.5;
--tracking-tight: -0.01em;
--tracking-normal: 0;
--tracking-wide: 0.02em;
--tracking-label: 0.08em;
/* Weight scale — one variable at each rung so intent is legible. */
--weight-regular: 400;
--weight-medium: 500;
--weight-semibold: 600;
--weight-bold: 700;
/* Spacing — strict 8pt grid (with 4pt half-step). */
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
--space-16: 64px;
--space-24: 96px;
/* Radii — three steps + pill. Softer than the old 4/8/12. */
--radius-sm: 6px;
--radius-md: 10px;
--radius-lg: 16px;
--radius-pill: 999px;
/* Elevation — two shadows only, ambient + raised. No glow rings. */
--shadow-ambient:
0 1px 2px rgba(0, 0, 0, 0.35),
0 4px 12px rgba(0, 0, 0, 0.22);
--shadow-raised:
0 2px 4px rgba(0, 0, 0, 0.4),
0 12px 28px rgba(0, 0, 0, 0.32);
/* Motion — standardized easings + durations. Apple-esque out-quart. */
--ease-standard: cubic-bezier(0.22, 1, 0.36, 1);
--ease-emphasized: cubic-bezier(0.16, 1, 0.3, 1);
--ease-linear: linear;
--dur-instant: 80ms;
--dur-fast: 160ms;
--dur-base: 240ms;
--dur-slow: 360ms;
--dur-slower: 560ms;
/* Focus ring — 2px accent, 2px offset, everywhere. */
--focus-ring: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent-primary);
/* ---------- 3. LEGACY ALIASES ----------
Old token names, mapped onto new semantic values so components that
haven't been rewritten yet keep rendering. Delete entries as pages
migrate; the block should be empty at the end of Phase 8. */
/* Old blue ramp names */
--blue-975: var(--gray-975);
--blue-925: var(--gray-950);
--blue-875: var(--gray-925);
--blue-825: var(--gray-850);
--blue-600: #256cae;
--blue-400: var(--blue-300);
/* Old spark/cherry/amber names */
--spark-500: var(--state-online);
--spark-400: #a8e5ff;
--spark-glow: 0 0 8px rgba(124, 212, 255, 0.45);
--spark-glow-lg: 0 0 14px rgba(124, 212, 255, 0.35);
--cherry-500: var(--state-danger);
--cherry-400: #ff7590;
--cherry-300: #ff9db0;
--amber-500: var(--state-warning);
--amber-glow: 0 0 8px rgba(255, 176, 32, 0.32);
--danger-500: var(--state-danger);
--success-500: #4ade80;
/* Old neutral names */
--cream: var(--text-primary);
--cream-dim: var(--gray-200);
--ink-100: var(--text-primary);
--ink-300: var(--text-secondary);
--ink-500: var(--text-tertiary);
--ink-700: var(--text-quaternary);
/* Old surface/border/accent aliases */
--bg-page: var(--surface-sunken);
--bg-nav: var(--surface);
--bg-card: var(--surface);
--bg-card-hi: var(--surface-raised);
--border: var(--border-subtle);
--border-hi: var(--border-strong);
--accent: var(--accent-primary);
--accent-hi: var(--accent-primary-hover);
--accent-glow: 0 0 0 1px rgba(46, 134, 209, 0.35);
--accent-glow-lg: 0 0 0 1px rgba(46, 134, 209, 0.28);
/* Old typography names */
--font-display: 'Alfa Slab One', 'Georgia', serif;
--text-mono: 13px;
--text-mono-sm: 12px;
--tracking-display: 0.12em;
--tracking-nav: 0.03em;
/* Old spacing/radii names */
--pad-card: var(--space-5);
--pad-panel: var(--space-6);
--pad-nav-y: var(--space-4);
--pad-nav-x: var(--space-8);
--gap-section: var(--space-8);
--gap-cards: var(--space-4);
--r-sm: var(--radius-sm);
--r-md: var(--radius-md);
--r-lg: var(--radius-lg);
--shadow-card: var(--shadow-ambient);
--shadow-panel: var(--shadow-raised);
}
/* Keyframes — pared down.
Legacy names kept so existing components animate; the new StatusPip
uses `pip-breathe` (opacity-only, GPU-cheap). */
@keyframes pip-breathe {
0%, 100% { opacity: 0.35; transform: scale(1); }
50% { opacity: 0.9; transform: scale(1.4); }
}
@keyframes shimmer {
from { background-position: -200% 0; }
to { background-position: 200% 0; }
}
/* Legacy — retained until ServerCard/BrandMark/StatusDot are replaced. */
@keyframes electric-pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(124, 212, 255, 0.5); }
50% { box-shadow: 0 0 0 5px rgba(124, 212, 255, 0); }
}
@keyframes cherry-fizz {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-1px); }
}
@keyframes spark-slide {
from { background-position: -100% 0; }
to { background-position: 200% 0; }
}
/* Reduced motion — neutralize all animation. Components that respect this
token get calm behavior for free; the media query below handles the
ones that don't. */
@media (prefers-reduced-motion: reduce) {
:root {
--dur-instant: 0.01ms;
--dur-fast: 0.01ms;
--dur-base: 0.01ms;
--dur-slow: 0.01ms;
--dur-slower: 0.01ms;
}
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}