import type { ThemeConfig, ThemeTokens } from "./config"; /** Turns a ThemeConfig + active scheme into the CSS variables Tailwind reads. */ export function tokensToCssVars( theme: ThemeConfig, scheme: "light" | "dark", ): Record { const t: ThemeTokens = scheme === "dark" ? theme.dark : theme.light; return { "--c-bg": t.bg, "--c-surface": t.surface, "--c-panel": t.panel, "--c-elevated": t.elevated, "--c-hover": t.hover, "--c-border": t.border, "--c-border-strong": t.borderStrong, "--c-ink": t.ink, "--c-muted": t.muted, "--c-dim": t.dim, "--c-accent": t.accent, "--c-accent-fg": t.accentFg, "--c-accent-soft": t.accentSoft, "--c-success": t.success, "--c-warning": t.warning, "--c-danger": t.danger, "--c-info": t.info, "--r-card": theme.radiusCard, "--r-control": theme.radiusControl, "--r-pill": theme.radiusPill, "--font-sans": theme.fontSans, "--font-mono": theme.fontMono, "--brand-gradient": theme.gradient, }; } /** Imperatively apply theme vars + the `dark` class to . */ export function applyTheme( el: HTMLElement, theme: ThemeConfig, scheme: "light" | "dark", ): void { const vars = tokensToCssVars(theme, scheme); for (const [k, v] of Object.entries(vars)) el.style.setProperty(k, v); el.classList.toggle("dark", scheme === "dark"); el.style.colorScheme = scheme; } /** Resolve a "system" mode against the OS preference. */ export function resolveScheme(mode: ThemeConfig["mode"]): "light" | "dark" { if (mode === "light" || mode === "dark") return mode; if (typeof window !== "undefined" && window.matchMedia) { return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; } return "dark"; } /** SSR-safe inline style string for the initial render (prevents flash). */ export function themeStyleString( theme: ThemeConfig, scheme: "light" | "dark", ): string { return Object.entries(tokensToCssVars(theme, scheme)) .map(([k, v]) => `${k}:${v}`) .join(";"); }