66 lines
2.0 KiB
TypeScript
66 lines
2.0 KiB
TypeScript
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<string, string> {
|
|
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 <html>. */
|
|
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 <html> render (prevents flash). */
|
|
export function themeStyleString(
|
|
theme: ThemeConfig,
|
|
scheme: "light" | "dark",
|
|
): string {
|
|
return Object.entries(tokensToCssVars(theme, scheme))
|
|
.map(([k, v]) => `${k}:${v}`)
|
|
.join(";");
|
|
}
|