🎨 fix(web): align UI and charts with theme tokens and presets

Improve theme switching fidelity (including system preference), extend design tokens so color presets tint real surfaces—not only primary/chrome—and refactor shared badges, tables, and dashboard visuals to semantic colors. Wire VChart series colors to `--chart-*` with safe fallbacks.

**Changes**

- **Theme runtime** (`theme-provider.tsx`): Validate stored theme cookie; keep `resolvedTheme` in sync with DOM + `(prefers-color-scheme)`; `resetTheme` respects `defaultTheme`; memoized context value.
- **Tokens** (`theme.css`): Add `--success|warning|info|neutral` (+ foregrounds) and map them under `@theme inline` for Tailwind utilities.
- **Presets** (`theme-presets.css`): For non-`default` presets, derive `card`, `popover`, `muted`, `accent`, `border`, `input`, and sidebar tokens from `--primary`/`--background`; map semantic status colors to preset chart variables.
- **Components**: `status-badge`, `colors` (avatars, announcements), `copy-button`, `group-badge`, `data-table` row styles, `sidebar` outline shadow (fix `var(--sidebar-border)` usage), ai-elements tool/web-preview status colors.
- **Dashboard**: Latency/API helpers and overview fragments use semantic tokens; `charts.ts` reads `--chart-1`…`--chart-5` from computed styles with fallbacks; `processChartData` / `processUserChartData` accept optional `themeKey` for preset churn; chart components pass `customization.preset` and bump `VChart` keys.

**Verification**

- `bun run typecheck`
This commit is contained in:
t0ng7u
2026-05-07 11:20:43 +08:00
parent 415d21d071
commit a7475a1e67
19 changed files with 315 additions and 172 deletions
+43
View File
@@ -273,6 +273,49 @@
--sidebar-ring: oklch(0.6359 0.1699 307.95);
}
/* ── Semantic surface bridge ──────────────────────────────────────────── */
/* Color presets should tint the surfaces most components actually use, not
* only primary buttons. These derived tokens keep the app theme-aware without
* duplicating per-component dark-mode overrides. */
[data-theme-preset]:not([data-theme-preset='default']) {
--card: color-mix(in oklch, var(--primary) 3%, var(--background));
--popover: color-mix(in oklch, var(--primary) 5%, var(--background));
--muted: color-mix(in oklch, var(--primary) 7%, var(--background));
--muted-foreground: color-mix(
in oklch,
var(--foreground) 68%,
var(--primary)
);
--accent: color-mix(in oklch, var(--primary) 14%, var(--background));
--accent-foreground: var(--foreground);
--border: color-mix(in oklch, var(--primary) 20%, var(--background));
--input: color-mix(in oklch, var(--primary) 22%, var(--background));
--sidebar: color-mix(in oklch, var(--primary) 4%, var(--background));
--sidebar-accent: color-mix(in oklch, var(--primary) 14%, var(--background));
--sidebar-accent-foreground: var(--foreground);
--sidebar-border: color-mix(in oklch, var(--primary) 18%, var(--background));
--success: var(--chart-2);
--warning: var(--chart-4);
--info: var(--chart-1);
--neutral: var(--muted-foreground);
}
.dark [data-theme-preset]:not([data-theme-preset='default']) {
--card: color-mix(in oklch, var(--primary) 8%, var(--background));
--popover: color-mix(in oklch, var(--primary) 12%, var(--background));
--muted: color-mix(in oklch, var(--primary) 12%, var(--background));
--muted-foreground: color-mix(
in oklch,
var(--foreground) 74%,
var(--primary)
);
--accent: color-mix(in oklch, var(--primary) 18%, var(--background));
--border: color-mix(in oklch, var(--primary) 24%, var(--background));
--input: color-mix(in oklch, var(--primary) 28%, var(--background));
--sidebar: color-mix(in oklch, var(--primary) 5%, var(--background));
--sidebar-accent: color-mix(in oklch, var(--primary) 18%, var(--background));
--sidebar-border: color-mix(in oklch, var(--primary) 22%, var(--background));
}
/* ── Border radius ────────────────────────────────────────────────────── */
[data-theme-radius='none'] {
--radius: 0rem;