feat(app): CSS-variable theming with dark/light palettes and accents

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-14 09:39:07 +07:00
parent b9f46a407d
commit dc95381870
+92 -23
View File
@@ -2,36 +2,105 @@ import type { SurfaceState } from "./layoutTypes";
/** Design tokens — mirror of DOCS/space-sh.pen variables. Single source for the UI. */ /** Design tokens — mirror of DOCS/space-sh.pen variables. Single source for the UI. */
export const COLORS = { export const COLORS = {
accent: "#4C8DFF", accent: "var(--c-accent)",
bgApp: "#0E1116", bgApp: "var(--c-bg-app)",
bgElevated: "#1A2029", bgElevated: "var(--c-bg-elevated)",
bgHover: "#222A35", bgHover: "var(--c-bg-hover)",
bgPanel: "#0A0D12", bgPanel: "var(--c-bg-panel)",
bgSidebar: "#13171F", bgSidebar: "var(--c-bg-sidebar)",
borderStrong: "#323C49", borderStrong: "var(--c-border-strong)",
borderSubtle: "#232A33", borderSubtle: "var(--c-border-subtle)",
textPrimary: "#E6EDF3", textPrimary: "var(--c-text-primary)",
textSecondary: "#8B97A6", textSecondary: "var(--c-text-secondary)",
textMuted: "#5A6573", textMuted: "var(--c-text-muted)",
stWork: "#4C8DFF", stWork: "var(--c-st-work)",
stWait: "#F2B84B", stWait: "var(--c-st-wait)",
stDone: "#3FB950", stDone: "var(--c-st-done)",
stError: "#F4544E", stError: "var(--c-st-error)",
stIdle: "#5A6573", stIdle: "var(--c-st-idle)",
searchMatch: "#5A4A1F", searchMatch: "var(--c-search-match)",
} as const; } as const;
export const FONT = { export const FONT = {
ui: "Inter, system-ui, sans-serif", ui: "Inter, system-ui, sans-serif",
mono: "'JetBrains Mono Variable', 'JetBrains Mono', monospace", mono: "'JetBrains Mono Variable', 'JetBrains Mono', monospace",
} as const; } as const;
/** Status color by surface state, plus the stopped pseudo-state. */ /** Status color by surface state, plus the stopped pseudo-state. */
export const STATE_COLOR: Record<SurfaceState | "stopped", string> = { export const STATE_COLOR: Record<SurfaceState | "stopped", string> = {
work: COLORS.stWork, work: COLORS.stWork,
wait: COLORS.stWait, wait: COLORS.stWait,
done: COLORS.stDone, done: COLORS.stDone,
error: COLORS.stError, error: COLORS.stError,
idle: COLORS.stIdle, idle: COLORS.stIdle,
stopped: COLORS.stIdle, stopped: COLORS.stIdle,
}; };
// ---------------------------------------------------------------------------
// Palettes
// ---------------------------------------------------------------------------
type Palette = Record<string, string>;
/** Dark palette — hex values identical to what COLORS contained before. */
const DARK: Palette = {
"bg-app": "#0E1116",
"bg-elevated": "#1A2029",
"bg-hover": "#222A35",
"bg-panel": "#0A0D12",
"bg-sidebar": "#13171F",
"border-strong": "#323C49",
"border-subtle": "#232A33",
"text-primary": "#E6EDF3",
"text-secondary": "#8B97A6",
"text-muted": "#5A6573",
"st-work": "#4C8DFF",
"st-wait": "#F2B84B",
"st-done": "#3FB950",
"st-error": "#F4544E",
"st-idle": "#5A6573",
"search-match": "#5A4A1F",
};
/** Light palette — a readable counterpart for every token. */
const LIGHT: Palette = {
"bg-app": "#F5F7FA",
"bg-elevated": "#FFFFFF",
"bg-hover": "#E8EDF3",
"bg-panel": "#EBEEF3",
"bg-sidebar": "#DDE3EC",
"border-strong": "#B0BAC7",
"border-subtle": "#CDD4DE",
"text-primary": "#0D1117",
"text-secondary": "#4A5568",
"text-muted": "#8898AA",
"st-work": "#2266CC",
"st-wait": "#C07800",
"st-done": "#1A7A30",
"st-error": "#C4231F",
"st-idle": "#8898AA",
"search-match": "#FDE68A",
};
export const ACCENTS: Record<string, string> = {
blue: "#4C8DFF",
teal: "#34D3C2",
purple: "#9B7BFF",
green: "#3FB950",
orange: "#F2934B",
};
export type ThemeName = "dark" | "light";
/** Real color values for consumers that can't use var() (xterm). Keys are the kebab tokens plus "accent". */
export function resolvePalette(theme: ThemeName, accent: string): Record<string, string> {
const base = theme === "light" ? LIGHT : DARK;
return { ...base, accent: ACCENTS[accent] ?? ACCENTS.blue };
}
/** Write the active palette to :root as --c-* custom properties. */
export function applyTheme(theme: ThemeName, accent: string): void {
const p = resolvePalette(theme, accent);
const root = document.documentElement.style;
for (const [k, v] of Object.entries(p)) root.setProperty(`--c-${k}`, v);
}