flex-theme
Version:
A flexible theming system for web applications
104 lines (103 loc) • 2.94 kB
JavaScript
// src/theme.ts
var currentTheme = "system";
var listeners = [];
function safeMatchMedia(query) {
if (typeof window === "undefined") return false;
if (typeof window.matchMedia !== "function") return false;
return window.matchMedia(query).matches;
}
function initTheme() {
if (typeof window === "undefined") return;
const savedTheme = localStorage.getItem("theme");
if (savedTheme && ["light", "dark", "system"].includes(savedTheme)) {
currentTheme = savedTheme;
} else {
currentTheme = "system";
}
applyTheme();
}
function applyTheme() {
if (typeof window === "undefined" || !document) return;
let effectiveTheme = currentTheme;
if (effectiveTheme === "system") {
effectiveTheme = safeMatchMedia("(prefers-color-scheme: dark)") ? "dark" : "light";
}
if (effectiveTheme === "dark") {
document.documentElement.classList.add("dark");
document.documentElement.classList.remove("light");
} else {
document.documentElement.classList.add("light");
document.documentElement.classList.remove("dark");
}
if (typeof localStorage !== "undefined") {
localStorage.setItem("theme", currentTheme);
}
}
function getTheme() {
return currentTheme;
}
function getResolvedTheme() {
if (currentTheme === "system") {
return safeMatchMedia("(prefers-color-scheme: dark)") ? "dark" : "light";
}
return currentTheme;
}
function setTheme(theme) {
if (!["light", "dark", "system"].includes(theme)) {
console.error(`Invalid theme: ${theme}. Must be 'light', 'dark', or 'system'`);
return;
}
currentTheme = theme;
applyTheme();
listeners.forEach((listener) => listener(theme));
}
function toggleTheme() {
const newTheme = currentTheme === "light" ? "dark" : "light";
setTheme(newTheme);
}
function onThemeChange(listener) {
listeners.push(listener);
}
function offThemeChange(listener) {
const index = listeners.indexOf(listener);
if (index !== -1) {
listeners.splice(index, 1);
}
}
if (typeof window !== "undefined") {
initTheme();
if (typeof window.matchMedia === "function") {
try {
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
if (mediaQuery.addEventListener) {
mediaQuery.addEventListener("change", () => {
if (currentTheme === "system") {
applyTheme();
listeners.forEach((listener) => listener(currentTheme));
}
});
} else if (mediaQuery.addListener) {
mediaQuery.addListener(() => {
if (currentTheme === "system") {
applyTheme();
listeners.forEach((listener) => listener(currentTheme));
}
});
}
} catch (e) {
console.error("Error setting up media query listener:", e);
}
}
}
var ThemeProvider = ({ children }) => {
return children;
};
export {
ThemeProvider,
getResolvedTheme,
getTheme,
offThemeChange,
onThemeChange,
setTheme,
toggleTheme
};