evOo-local/apps/web/src/lib/ui-appearance.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

68 lines
2.2 KiB
TypeScript

import {
type AppearancePreference,
persistAppearancePreference,
type ResolvedAppearance,
resolveAppearance,
resolveAppearancePreference,
tokensForAppearance,
} from "@rakazo/ui-tokens";
export type { AppearancePreference, ResolvedAppearance };
const THEME_COLOR_META = 'meta[name="theme-color"]';
export function readSystemAppearance(
media: Pick<MediaQueryList, "matches"> | null = typeof window !== "undefined"
? window.matchMedia("(prefers-color-scheme: light)")
: null,
): ResolvedAppearance {
return media?.matches ? "light" : "dark";
}
export function applyResolvedAppearance(
appearance: ResolvedAppearance,
root: HTMLElement | null = typeof document !== "undefined" ? document.documentElement : null,
): void {
if (!root) return;
root.dataset.theme = appearance;
root.style.colorScheme = appearance;
if (typeof document === "undefined") return;
const meta = document.querySelector(THEME_COLOR_META);
if (meta) {
meta.setAttribute("content", tokensForAppearance(appearance).background);
}
}
export function applyUiAppearance(
preference: AppearancePreference = resolveAppearancePreference(),
system: ResolvedAppearance = readSystemAppearance(),
): ResolvedAppearance {
const resolved = resolveAppearance(preference, system);
applyResolvedAppearance(resolved);
return resolved;
}
export function setUiAppearance(preference: AppearancePreference): ResolvedAppearance {
persistAppearancePreference(preference);
return applyUiAppearance(preference);
}
export function getUiAppearancePreference(): AppearancePreference {
return resolveAppearancePreference();
}
/** Keep `data-theme` in sync when the OS scheme changes and preference is System. */
export function watchSystemAppearance(
onChange: (system: ResolvedAppearance) => void = () => {
if (resolveAppearancePreference() === "system") applyUiAppearance("system");
},
): () => void {
if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
return () => undefined;
}
const media = window.matchMedia("(prefers-color-scheme: light)");
const handler = () => onChange(media.matches ? "light" : "dark");
media.addEventListener("change", handler);
return () => media.removeEventListener("change", handler);
}