feat(redesign): design-system tokens, 23 Vue components, game-aware shell + Fleet/Solo dashboard
All checks were successful
Test Asgard Runner / test (push) Successful in 4s

Tokens ported 1:1 from the Claude Design bundle (colors/game-themes/type/spacing/elevation/motion/fonts) with the data-theme/data-game theming contract via useThemeGame (+ cc-skin-swap repaint guard). 23 design-system components reimplemented as Vue SFCs (core/forms/data/navigation/feedback/brand). DashboardLayout rebuilt as the game-aware shell (GameSwitcher, grouped nav with permission gating preserved, agent-health footer, topbar). DashboardView: Fleet + Solo with per-game GAME_FIELDS rows and the themed ECharts PlayersChart; Solo wired to the real server store, Fleet on representative data pending the multi-instance backend. All four game skins (Rust/Dune/Conan/Soulmask). vue-tsc + vite build green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell
2026-06-11 02:12:35 -04:00
parent ef128b47d2
commit f91ef84832
42 changed files with 3577 additions and 299 deletions

View File

@@ -0,0 +1,15 @@
/* ============================================================
Corrosion Control — Design System
Root stylesheet. Consumers link THIS one file.
Import order matters: fonts → primitives → colors → game themes
→ base. (base.css references color/accent tokens.)
============================================================ */
@import url("tokens/fonts.css");
@import url("tokens/spacing.css");
@import url("tokens/typography.css");
@import url("tokens/motion.css");
@import url("tokens/colors.css");
@import url("tokens/game-themes.css");
@import url("tokens/elevation.css");
@import url("tokens/base.css");

View File

@@ -0,0 +1,75 @@
/* ============================================================
Corrosion Control — Base / Reset
Minimal, opinionated. Applies the token system to bare HTML so
specimen cards and kits inherit the look without boilerplate.
============================================================ */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
font-family: var(--font-sans);
font-size: var(--text-base);
line-height: var(--leading-normal);
color: var(--text-primary);
background-color: var(--surface-canvas);
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
font-feature-settings: "cv01", "ss01";
}
h1, h2, h3, h4, h5, h6, p, figure { margin: 0; }
a { color: inherit; text-decoration: none; }
::selection {
background: var(--accent-soft-strong);
color: var(--text-primary);
}
/* Tabular numbers everywhere numbers matter */
.tnum { font-variant-numeric: tabular-nums; }
/* Custom scrollbars — quiet, on-brand */
* {
scrollbar-width: thin;
scrollbar-color: var(--border-strong) transparent;
}
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
background: var(--border-strong);
border-radius: var(--radius-pill);
border: 2px solid transparent;
background-clip: content-box;
}
*::-webkit-scrollbar-thumb:hover { background: var(--text-muted); background-clip: content-box; }
/* Focus-visible default */
:focus-visible {
outline: none;
box-shadow: var(--focus-ring);
}
/* Reduced motion guard */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* ---- Skin-swap repaint guard ----
When flipping data-game or data-theme on the root, add the .cc-skin-swap
class for ONE frame. This suppresses transitions during the swap so every
accent-consuming surface repaints immediately — works around a Chrome
custom-property + transition staleness where elements that both read
var(--accent)/var(--accent-text) AND have a color/background transition keep
the old accent until the next reflow. See readme "Theming contract". */
.cc-skin-swap *,
.cc-skin-swap *::before,
.cc-skin-swap *::after { transition: none !important; }

View File

@@ -0,0 +1,136 @@
/* ============================================================
Corrosion Control — Color System
------------------------------------------------------------
Three layers:
1. Raw neutral ramp (absolute; identical in both themes)
2. Semantic surface / text / border tokens (flip per theme)
3. Status colors (online/offline/warn/info/...) — game-agnostic
Game ACCENT colors live in game-themes.css.
Theme + game are set together on <html>:
<html data-theme="dark" data-game="rust">
Dark is primary. Light is full-parity.
============================================================ */
:root {
/* ---- Raw neutral ramp (warm-cool slate, absolute) ---- */
--n-0: #ffffff;
--n-25: #fafbfc;
--n-50: #f3f5f7;
--n-100: #e8ebef;
--n-150: #dce0e6;
--n-200: #ccd2da;
--n-300: #aeb6c1;
--n-400: #8a929e;
--n-500: #6b7280;
--n-600: #515862;
--n-650: #444a53;
--n-700: #363b43;
--n-750: #2b2f36;
--n-800: #1f2329;
--n-850: #181b20;
--n-900: #121419;
--n-925: #0e0f13;
--n-950: #0a0b0e;
--n-975: #060709;
/* ---- Semantic: DARK (default) ---- */
--surface-canvas: var(--n-950); /* app background */
--surface-sunken: var(--n-975); /* wells, deep insets */
--surface-base: var(--n-925); /* primary panels */
--surface-raised: var(--n-850); /* cards, rows */
--surface-raised-2:var(--n-800); /* nested cards, hover cards */
--surface-overlay: var(--n-800); /* menus, popovers, dialogs */
--surface-inset: #07080a; /* inputs, console, code */
--surface-hover: rgba(255, 255, 255, 0.045);
--surface-active: rgba(255, 255, 255, 0.075);
--surface-selected:rgba(255, 255, 255, 0.06);
--border-subtle: rgba(255, 255, 255, 0.06);
--border-default: rgba(255, 255, 255, 0.10);
--border-strong: rgba(255, 255, 255, 0.16);
--text-primary: #f2f4f7;
--text-secondary: #aeb4bf;
--text-tertiary: #767d89;
--text-muted: #565d68;
--text-inverse: #0a0b0e;
--text-on-accent: var(--accent-contrast);
/* Scrim for modals / image overlays */
--scrim: rgba(4, 5, 7, 0.66);
/* ---- Status / semantic (consistent across themes) ---- */
--status-online: #36c780;
--status-online-soft: rgba(54, 199, 128, 0.14);
--status-online-border: rgba(54, 199, 128, 0.38);
--status-offline: #e5484d;
--status-offline-soft: rgba(229, 72, 77, 0.14);
--status-offline-border: rgba(229, 72, 77, 0.40);
--status-warn: #e8a33c;
--status-warn-soft: rgba(232, 163, 60, 0.15);
--status-warn-border: rgba(232, 163, 60, 0.40);
--status-info: #4c8df0;
--status-info-soft: rgba(76, 141, 240, 0.15);
--status-info-border: rgba(76, 141, 240, 0.40);
--status-starting: #2bc2d4; /* booting / updating / restarting */
--status-starting-soft: rgba(43, 194, 212, 0.15);
--status-starting-border: rgba(43, 194, 212, 0.40);
--status-wiping: #9b7bf0; /* Rust map wipe / maintenance */
--status-wiping-soft: rgba(155, 123, 240, 0.16);
--status-wiping-border: rgba(155, 123, 240, 0.40);
/* Aliases used by components */
--success: var(--status-online);
--danger: var(--status-offline);
--warning: var(--status-warn);
--info: var(--status-info);
/* Data-viz categorical (ECharts-friendly) */
--viz-1: var(--accent);
--viz-2: #4c8df0;
--viz-3: #36c780;
--viz-4: #9b7bf0;
--viz-5: #2bc2d4;
--viz-6: #e8a33c;
--viz-grid: var(--border-subtle);
}
/* ---- Semantic: LIGHT (full parity) ---- */
[data-theme="light"] {
--surface-canvas: #f5f6f8;
--surface-sunken: #eceef1;
--surface-base: #ffffff;
--surface-raised: #ffffff;
--surface-raised-2:#fbfcfd;
--surface-overlay: #ffffff;
--surface-inset: #f1f3f5;
--surface-hover: rgba(12, 16, 22, 0.04);
--surface-active: rgba(12, 16, 22, 0.07);
--surface-selected:rgba(12, 16, 22, 0.05);
--border-subtle: rgba(12, 16, 22, 0.07);
--border-default: rgba(12, 16, 22, 0.12);
--border-strong: rgba(12, 16, 22, 0.20);
--text-primary: #14171c;
--text-secondary: #474e58;
--text-tertiary: #6b727e;
--text-muted: #969ca6;
--text-inverse: #ffffff;
--scrim: rgba(16, 20, 28, 0.45);
/* Status soft fills need a touch more alpha on light */
--status-online-soft: rgba(33, 160, 98, 0.12);
--status-offline-soft: rgba(206, 44, 49, 0.10);
--status-warn-soft: rgba(193, 124, 18, 0.13);
--status-info-soft: rgba(43, 105, 214, 0.10);
--status-starting-soft: rgba(18, 150, 168, 0.12);
--status-wiping-soft: rgba(118, 86, 214, 0.12);
}

View File

@@ -0,0 +1,40 @@
/* ============================================================
Corrosion Control — Elevation, Borders, Glows
Shadows are quiet on dark surfaces; depth comes from layered
fills + hairline borders. Accent "glow" is reserved for
active/live states and game-themed focus.
============================================================ */
:root {
/* Hairline rings (use as box-shadow inset or border) */
--ring-subtle: inset 0 0 0 1px var(--border-subtle);
--ring-default: inset 0 0 0 1px var(--border-default);
--ring-strong: inset 0 0 0 1px var(--border-strong);
/* Drop shadows — tuned for dark; subtle and tight */
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.35);
--shadow-sm: 0 2px 6px rgba(0, 0, 0, 0.40);
--shadow-md: 0 6px 18px -4px rgba(0, 0, 0, 0.50);
--shadow-lg: 0 18px 40px -10px rgba(0, 0, 0, 0.60);
--shadow-xl: 0 32px 70px -16px rgba(0, 0, 0, 0.70);
/* Popover / menu — combines ring + drop for crisp edges on dark */
--shadow-pop: 0 0 0 1px var(--border-default), 0 12px 36px -8px rgba(0, 0, 0, 0.66);
/* Accent glow — game-themed; used on live/active controls & focus */
--glow-accent: 0 0 0 1px var(--accent-border), 0 0 22px -2px var(--accent-glow);
--glow-accent-sm: 0 0 14px -2px var(--accent-glow);
--glow-online: 0 0 12px -1px rgba(54, 199, 128, 0.55);
/* Focus ring */
--focus-ring: 0 0 0 2px var(--surface-canvas), 0 0 0 4px var(--accent);
}
[data-theme="light"] {
--shadow-xs: 0 1px 2px rgba(16, 20, 28, 0.06);
--shadow-sm: 0 2px 6px rgba(16, 20, 28, 0.08);
--shadow-md: 0 8px 20px -6px rgba(16, 20, 28, 0.12);
--shadow-lg: 0 20px 44px -12px rgba(16, 20, 28, 0.16);
--shadow-xl: 0 32px 70px -18px rgba(16, 20, 28, 0.20);
--shadow-pop: 0 0 0 1px var(--border-default), 0 14px 38px -10px rgba(16, 20, 28, 0.20);
}

View File

@@ -0,0 +1,21 @@
/* ============================================================
Corrosion Control — Fonts
Geist — UI / body / app headings
JetBrains Mono — console, data, IDs, telemetry
Oxanium — brand wordmark + marketing display (game-ops flavor)
------------------------------------------------------------
NOTE: Loaded from Google Fonts CDN. If you want these self-
hosted (offline), send the woff2 files and these @imports
become @font-face rules.
============================================================ */
@import url('https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600;700;800&family=JetBrains+Mono:ital,wght@0,400;0,500;0,600;0,700;1,400&family=Oxanium:wght@500;600;700;800&display=swap');
:root {
--font-sans: 'Geist', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
--font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', 'Cascadia Code', Menlo, monospace;
/* Brand wordmark + big marketing display — squared, technical, gamey */
--font-brand: 'Oxanium', 'Geist', system-ui, sans-serif;
/* App-level display headings stay on the neutral sans */
--font-display: var(--font-sans);
}

View File

@@ -0,0 +1,150 @@
/* ============================================================
Corrosion Control — Game Themes (the re-skin layer)
------------------------------------------------------------
The shell is neutral. Each game declares an ACCENT ramp + an
ATMOSPHERE (backdrop hues for hero headers, login, glows).
Set on <html data-game="rust"> (or dune / ark / valheim / ...).
Default (no data-game) = Corrosion brand = Oxide Orange.
Accent contract every game must define:
--accent base fill
--accent-hover hover fill
--accent-press pressed / darker
--accent-contrast text/icon ON the accent fill
--accent-text accent used AS text on dark surfaces (lightened for AA)
--accent-soft low-alpha tint background
--accent-soft-strong
--accent-border accent hairline
--accent-glow glow color (box-shadow)
--game-label printable name
--atmo-1 / --atmo-2 backdrop gradient stops
--atmo-haze radial haze color
============================================================ */
/* ---------- Default brand / RUST — Oxide Orange #F26622 ---------- */
:root,
[data-game="rust"] {
--accent: #f26622;
--accent-hover: #ff7a38;
--accent-press: #d9550f;
--accent-contrast: #190d05;
--accent-text: #ff8a4c;
--accent-soft: rgba(242, 102, 34, 0.13);
--accent-soft-strong: rgba(242, 102, 34, 0.22);
--accent-border: rgba(242, 102, 34, 0.42);
--accent-glow: rgba(242, 102, 34, 0.50);
--game-label: "Rust"; /* @kind other */
--atmo-1: #2c1206;
--atmo-2: #0a0b0e;
--atmo-haze: rgba(242, 102, 34, 0.30);
}
/* ---------- DUNE: AWAKENING — Spice Amber / desert gold ---------- */
[data-game="dune"] {
--accent: #e9a53a;
--accent-hover: #f7b94f;
--accent-press: #c9851f;
--accent-contrast: #1c1303;
--accent-text: #f0bc5e;
--accent-soft: rgba(233, 165, 58, 0.14);
--accent-soft-strong: rgba(233, 165, 58, 0.24);
--accent-border: rgba(233, 165, 58, 0.44);
--accent-glow: rgba(233, 165, 58, 0.46);
--game-label: "Dune: Awakening"; /* @kind other */
--atmo-1: #2c1e08;
--atmo-2: #0a0b0e;
--atmo-haze: rgba(233, 165, 58, 0.30);
}
/* ---------- SOULMASK: Ritual Jade ---------- */
[data-game="soulmask"] {
--accent: #43c47e;
--accent-hover: #59d792;
--accent-press: #2c9c5f;
--accent-contrast: #04140c;
--accent-text: #63d894;
--accent-soft: rgba(67, 196, 126, 0.14);
--accent-soft-strong: rgba(67, 196, 126, 0.24);
--accent-border: rgba(67, 196, 126, 0.42);
--accent-glow: rgba(67, 196, 126, 0.46);
--game-label: "Soulmask"; /* @kind other */
--atmo-1: #08231a;
--atmo-2: #0a0b0e;
--atmo-haze: rgba(67, 196, 126, 0.26);
}
/* ---------- CONAN EXILES: Hyborian Bronze ---------- */
[data-game="conan"] {
--accent: #bb7637;
--accent-hover: #d28d4b;
--accent-press: #985c24;
--accent-contrast: #160d03;
--accent-text: #d59a5e;
--accent-soft: rgba(187, 118, 55, 0.15);
--accent-soft-strong: rgba(187, 118, 55, 0.26);
--accent-border: rgba(187, 118, 55, 0.44);
--accent-glow: rgba(187, 118, 55, 0.46);
--game-label: "Conan Exiles"; /* @kind other */
--atmo-1: #2a1b0b;
--atmo-2: #0a0b0e;
--atmo-haze: rgba(187, 118, 55, 0.30);
}
/* ---------- Room to add more (stubs, ready to ship) ---------- */
[data-game="ark"] {
--accent: #36c2a8;
--accent-hover: #4ad7bd;
--accent-press: #1f9c86;
--accent-contrast: #04140f;
--accent-text: #54dcc2;
--accent-soft: rgba(54, 194, 168, 0.14);
--accent-soft-strong: rgba(54, 194, 168, 0.24);
--accent-border: rgba(54, 194, 168, 0.42);
--accent-glow: rgba(54, 194, 168, 0.46);
--game-label: "ARK"; /* @kind other */
--atmo-1: #07241f;
--atmo-2: #0a0b0e;
--atmo-haze: rgba(54, 194, 168, 0.26);
}
[data-game="valheim"] {
--accent: #5b9bf0;
--accent-hover: #74afff;
--accent-press: #3c7ad4;
--accent-contrast: #050d1a;
--accent-text: #7fb2ff;
--accent-soft: rgba(91, 155, 240, 0.14);
--accent-soft-strong: rgba(91, 155, 240, 0.24);
--accent-border: rgba(91, 155, 240, 0.42);
--accent-glow: rgba(91, 155, 240, 0.46);
--game-label: "Valheim"; /* @kind other */
--atmo-1: #0a1c2e;
--atmo-2: #0a0b0e;
--atmo-haze: rgba(91, 155, 240, 0.26);
}
[data-game="palworld"] {
--accent: #58b6e8;
--accent-hover: #71c8f6;
--accent-press: #3a92c4;
--accent-contrast: #04121b;
--accent-text: #7fcaf2;
--accent-soft: rgba(88, 182, 232, 0.14);
--accent-soft-strong: rgba(88, 182, 232, 0.24);
--accent-border: rgba(88, 182, 232, 0.42);
--accent-glow: rgba(88, 182, 232, 0.46);
--game-label: "Palworld"; /* @kind other */
--atmo-1: #08222e;
--atmo-2: #0a0b0e;
--atmo-haze: rgba(88, 182, 232, 0.26);
}
/* ---------- Light-theme accent legibility ----------
On light surfaces, accent-as-text reads better at the pressed
(darker) value. Placed last so it wins for --accent-text when
data-theme="light" and data-game=* are both on <html>. */
[data-theme="light"] {
--accent-text: var(--accent-press);
--accent-soft: color-mix(in srgb, var(--accent) 12%, transparent);
--accent-soft-strong: color-mix(in srgb, var(--accent) 20%, transparent);
}

View File

@@ -0,0 +1,27 @@
/* ============================================================
Corrosion Control — Motion
Fast, mechanical, precise. No bounce on chrome. Subtle spring
reserved for "live" telemetry (pulses, meters). Respect
prefers-reduced-motion in components.
============================================================ */
:root {
--dur-instant: 80ms; /* @kind other */
--dur-fast: 120ms; /* @kind other */
--dur-base: 170ms; /* @kind other */
--dur-slow: 240ms; /* @kind other */
--dur-slower: 360ms; /* @kind other */
/* Easings */
--ease-standard: cubic-bezier(0.2, 0, 0, 1); /* @kind other */
--ease-out: cubic-bezier(0.16, 1, 0.3, 1); /* @kind other */
--ease-in: cubic-bezier(0.5, 0, 0.84, 0); /* @kind other */
--ease-emphasized: cubic-bezier(0.34, 1.4, 0.5, 1); /* @kind other */
/* Common transition bundles */
--transition-colors: color var(--dur-fast) var(--ease-standard),
background-color var(--dur-fast) var(--ease-standard),
border-color var(--dur-fast) var(--ease-standard),
box-shadow var(--dur-fast) var(--ease-standard);
--transition-transform: transform var(--dur-base) var(--ease-out);
}

View File

@@ -0,0 +1,50 @@
/* ============================================================
Corrosion Control — Spacing, Radius, Sizing
Dense ops-cockpit scale. 4px base grid, tightened.
============================================================ */
:root {
/* Space scale (px) — used for padding, gap, margins */
--space-0: 0;
--space-px: 1px;
--space-0-5: 2px;
--space-1: 4px;
--space-1-5: 6px;
--space-2: 8px;
--space-2-5: 10px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-7: 28px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
--space-14: 56px;
--space-16: 64px;
--space-20: 80px;
--space-24: 96px;
--space-32: 128px;
/* Radius — small & technical; cards stay crisp, not rounded-blobby */
--radius-xs: 3px;
--radius-sm: 5px;
--radius-md: 7px;
--radius-lg: 10px;
--radius-xl: 14px;
--radius-2xl: 20px;
--radius-pill: 999px;
/* Control heights — compact rows for data-dense UI */
--control-h-xs: 24px;
--control-h-sm: 30px;
--control-h-md: 36px;
--control-h-lg: 44px;
/* Layout primitives */
--sidebar-w: 248px;
--sidebar-w-collapsed: 64px;
--topbar-h: 56px;
--content-max: 1440px;
--container-pad: var(--space-6);
}

View File

@@ -0,0 +1,75 @@
/* ============================================================
Corrosion Control — Typography
Geist for UI & display; JetBrains Mono for telemetry, IDs,
console, and any numeric/code data. Dense reading sizes.
============================================================ */
:root {
/* Font sizes (px) — base UI is 14 (dense) */
--text-2xs: 11px;
--text-xs: 12px;
--text-sm: 13px;
--text-base: 14px;
--text-md: 15px;
--text-lg: 17px;
--text-xl: 20px;
--text-2xl: 24px;
--text-3xl: 30px;
--text-4xl: 38px;
--text-5xl: 50px;
--text-6xl: 66px;
--text-7xl: 88px;
/* Line heights */
--leading-none: 1;
--leading-tight: 1.15;
--leading-snug: 1.3;
--leading-normal: 1.5;
--leading-relaxed: 1.65;
/* Weights */
--weight-light: 300;
--weight-regular: 400;
--weight-medium: 500;
--weight-semibold: 600;
--weight-bold: 700;
--weight-black: 800;
/* Letter spacing */
--tracking-tighter: -0.03em;
--tracking-tight: -0.015em;
--tracking-normal: 0;
--tracking-wide: 0.02em;
--tracking-wider: 0.06em;
--tracking-caps: 0.10em; /* eyebrows / overlines / labels */
/* ---- Semantic roles (font shorthands via custom props) ---- */
--font-display: var(--font-sans);
}
/* ---- Optional helper classes (cards/kits can use these) ---- */
.t-display {
font-family: var(--font-display);
font-weight: var(--weight-bold);
letter-spacing: var(--tracking-tight);
line-height: var(--leading-tight);
}
.t-eyebrow {
font-family: var(--font-mono);
font-size: var(--text-xs);
font-weight: var(--weight-medium);
letter-spacing: var(--tracking-caps);
text-transform: uppercase;
color: var(--text-tertiary);
}
.t-mono {
font-family: var(--font-mono);
font-variant-numeric: tabular-nums;
letter-spacing: var(--tracking-normal);
}
.t-metric {
font-family: var(--font-mono);
font-weight: var(--weight-semibold);
font-variant-numeric: tabular-nums;
letter-spacing: var(--tracking-tight);
}