271 lines
8.8 KiB
CSS
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;
|
|
}
|
|
}
|