pluralchat / src /lib /switchTheme.ts
victor's picture
victor HF Staff
HuggingChat 2026 (#1875)
7bf1507 unverified
export type ThemePreference = "light" | "dark" | "system";
function setMetaThemeColor(isDark: boolean) {
const metaTheme = document.querySelector('meta[name="theme-color"]') as HTMLMetaElement | null;
if (!metaTheme) return;
metaTheme.setAttribute("content", isDark ? "rgb(26, 36, 50)" : "rgb(249, 250, 251)");
}
function applyDarkClass(isDark: boolean) {
const { classList } = document.querySelector("html") as HTMLElement;
if (isDark) classList.add("dark");
else classList.remove("dark");
setMetaThemeColor(isDark);
}
export function getThemePreference(): ThemePreference {
const raw = typeof localStorage !== "undefined" ? localStorage.getItem("theme") : null;
if (raw === "light" || raw === "dark" || raw === "system") return raw;
return "system";
}
/**
* Explicitly set the theme preference and apply it immediately.
* - "light": force light
* - "dark": force dark
* - "system": follow the OS preference
*/
export function setTheme(preference: ThemePreference) {
try {
localStorage.theme = preference;
} catch (_err) {
void 0; // ignore write errors
}
const mql = window.matchMedia("(prefers-color-scheme: dark)");
const resolve = () =>
applyDarkClass(preference === "dark" || (preference === "system" && mql.matches));
// Apply now
resolve();
// If following system, listen for changes; otherwise remove listener
const listener = () => resolve();
// Store on window to allow replacing listener later
const key = "__theme_mql_listener" as const;
const w = window as unknown as {
[key: string]: ((this: MediaQueryList, ev: MediaQueryListEvent) => void) | undefined;
};
const existing = w[key];
if (existing) {
try {
mql.removeEventListener("change", existing);
} catch (_err) {
// older Safari compatibility
const legacy = (
mql as unknown as {
removeListener?: (l: (this: MediaQueryList, ev: MediaQueryListEvent) => void) => void;
}
).removeListener;
legacy?.(existing);
}
w[key] = undefined;
}
if (preference === "system") {
try {
mql.addEventListener("change", listener);
} catch (_err) {
// older Safari compatibility
const legacy = (
mql as unknown as {
addListener?: (l: (this: MediaQueryList, ev: MediaQueryListEvent) => void) => void;
}
).addListener;
legacy?.(listener);
}
w[key] = listener;
}
}
// Backward-compatible toggle used by the sidebar button
export function switchTheme() {
const html = document.querySelector("html") as HTMLElement;
const isDark = html.classList.contains("dark");
const next: ThemePreference = isDark ? "light" : "dark";
setTheme(next);
}