feat(ui): design tokens + light/dark theming, migrate in-game chrome (F1a)
Tests · UI / test (push) Successful in 2m4s
Tests · UI / test (push) Successful in 2m4s
Introduce the shared design-token system under ui/frontend/src/lib/theme/: tokens.css (dark default + light palette, plus spacing/radii/typography scales), base.css global baseline (document background, text, token focus ring, selection), and theme.svelte.ts (system/light/dark choice, persisted to localStorage, applied via data-theme on <html>). A pre-paint guard in app.html resolves the theme before the app boots to avoid a flash, and the theme picker is wired into the previously-disabled account-menu stub. Migrate the always-visible in-game chrome to the tokens (header, account menu, sidebar, tab-bar, bottom-tabs, shell background): dark renders as before, light comes for free. The default stays dark during the incremental migration; the remaining view bodies migrate in F1b. Docs: ui/docs/design-system.md (+ index entry). Test: tests/theme.test.ts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Galaxy UI design tokens.
|
||||
*
|
||||
* The single source of every theme value in the client. Components must
|
||||
* reference these custom properties (`var(--color-…)`, `var(--space-…)`)
|
||||
* instead of literal hex/px so a palette change is a one-file edit and
|
||||
* the light/dark themes stay in sync.
|
||||
*
|
||||
* Structure:
|
||||
* :root theme-independent scales (space, radii,
|
||||
* typography) — identical in every theme.
|
||||
* :root, [data-theme=dark] the dark palette (also the default when no
|
||||
* theme is set, so first paint is never bare).
|
||||
* [data-theme=light] the light palette overrides.
|
||||
*
|
||||
* The resolved theme is written to `data-theme` on <html> by the
|
||||
* pre-paint guard in `app.html` and thereafter by
|
||||
* `$lib/theme/theme.svelte.ts`. See `ui/docs/design-system.md`.
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* Spacing scale (4px base). */
|
||||
--space-1: 0.25rem;
|
||||
--space-2: 0.5rem;
|
||||
--space-3: 0.75rem;
|
||||
--space-4: 1rem;
|
||||
--space-5: 1.5rem;
|
||||
--space-6: 2rem;
|
||||
--space-8: 3rem;
|
||||
|
||||
/* Corner radii. */
|
||||
--radius-sm: 4px;
|
||||
--radius-md: 6px;
|
||||
--radius-lg: 10px;
|
||||
--radius-pill: 999px;
|
||||
|
||||
/* Typography. */
|
||||
--font-sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
||||
--font-mono: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas,
|
||||
monospace;
|
||||
--text-xs: 0.75rem;
|
||||
--text-sm: 0.8125rem;
|
||||
--text-base: 0.875rem;
|
||||
--text-md: 1rem;
|
||||
--text-lg: 1.125rem;
|
||||
--text-xl: 1.375rem;
|
||||
--leading-tight: 1.25;
|
||||
--leading-normal: 1.5;
|
||||
--weight-normal: 400;
|
||||
--weight-medium: 500;
|
||||
--weight-semibold: 600;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dark palette — the project's native look and the default. Listed under
|
||||
* both :root and [data-theme=dark] so a document with no data-theme yet
|
||||
* (e.g. the pre-paint instant) still renders dark rather than unstyled.
|
||||
*/
|
||||
:root,
|
||||
:root[data-theme="dark"] {
|
||||
color-scheme: dark;
|
||||
|
||||
--color-bg: #0a0e1a;
|
||||
--color-surface: #0e1322;
|
||||
--color-surface-raised: #161d33;
|
||||
--color-surface-overlay: #161b2e;
|
||||
--color-surface-hover: #1c2238;
|
||||
|
||||
--color-border-subtle: #20253a;
|
||||
--color-border: #2a3150;
|
||||
--color-border-strong: #3a4470;
|
||||
|
||||
--color-text: #e8eaf6;
|
||||
--color-text-muted: #9aa4c6;
|
||||
--color-text-faint: #6b7396;
|
||||
|
||||
--color-accent: #6d8cff;
|
||||
--color-accent-hover: #88a0ff;
|
||||
--color-accent-active: #5a78f0;
|
||||
--color-accent-contrast: #0a0e1a;
|
||||
--color-accent-subtle: rgba(109, 140, 255, 0.14);
|
||||
|
||||
--color-danger: #e07a7a;
|
||||
--color-danger-contrast: #0a0e1a;
|
||||
--color-danger-subtle: rgba(224, 122, 122, 0.14);
|
||||
|
||||
--color-success: #5fb98c;
|
||||
--color-success-subtle: rgba(95, 185, 140, 0.14);
|
||||
|
||||
--color-warning: #e0b15a;
|
||||
--color-warning-subtle: rgba(224, 177, 90, 0.14);
|
||||
|
||||
--color-focus: var(--color-accent);
|
||||
|
||||
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.35);
|
||||
--shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
/* Light palette. */
|
||||
:root[data-theme="light"] {
|
||||
color-scheme: light;
|
||||
|
||||
--color-bg: #f3f5fb;
|
||||
--color-surface: #ffffff;
|
||||
--color-surface-raised: #f7f9fd;
|
||||
--color-surface-overlay: #ffffff;
|
||||
--color-surface-hover: #eaeef8;
|
||||
|
||||
--color-border-subtle: #e6eaf3;
|
||||
--color-border: #d3d9e8;
|
||||
--color-border-strong: #aeb8d4;
|
||||
|
||||
--color-text: #1a2138;
|
||||
--color-text-muted: #59617e;
|
||||
--color-text-faint: #8b93ad;
|
||||
|
||||
--color-accent: #4a63d8;
|
||||
--color-accent-hover: #3a52c8;
|
||||
--color-accent-active: #2f46b5;
|
||||
--color-accent-contrast: #ffffff;
|
||||
--color-accent-subtle: rgba(74, 99, 216, 0.1);
|
||||
|
||||
--color-danger: #c84d4d;
|
||||
--color-danger-contrast: #ffffff;
|
||||
--color-danger-subtle: rgba(200, 77, 77, 0.1);
|
||||
|
||||
--color-success: #2f8f63;
|
||||
--color-success-subtle: rgba(47, 143, 99, 0.12);
|
||||
|
||||
--color-warning: #b07d24;
|
||||
--color-warning-subtle: rgba(176, 125, 36, 0.14);
|
||||
|
||||
--color-focus: var(--color-accent);
|
||||
|
||||
--shadow-sm: 0 1px 2px rgba(20, 28, 51, 0.08);
|
||||
--shadow-md: 0 4px 12px rgba(20, 28, 51, 0.1);
|
||||
--shadow-lg: 0 8px 24px rgba(20, 28, 51, 0.14);
|
||||
}
|
||||
Reference in New Issue
Block a user