mirror of
https://github.com/Dvorinka/Bookra.git
synced 2026-06-04 04:22:59 +00:00
89 lines
2.2 KiB
TypeScript
89 lines
2.2 KiB
TypeScript
import {
|
|
createContext,
|
|
createEffect,
|
|
createSignal,
|
|
ParentComponent,
|
|
useContext,
|
|
} from "solid-js";
|
|
|
|
type Theme = "light" | "dark" | "system";
|
|
|
|
const STORAGE_KEY = "bookra-theme";
|
|
|
|
function getInitialTheme(): Theme {
|
|
if (typeof window === "undefined") return "system";
|
|
const stored = localStorage.getItem(STORAGE_KEY) as Theme | null;
|
|
return stored ?? "system";
|
|
}
|
|
|
|
function getResolvedTheme(theme: Theme): "light" | "dark" {
|
|
if (theme === "system") {
|
|
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
? "dark"
|
|
: "light";
|
|
}
|
|
return theme;
|
|
}
|
|
|
|
type ThemeContextValue = {
|
|
theme: () => Theme;
|
|
resolvedTheme: () => "light" | "dark";
|
|
setTheme: (theme: Theme) => void;
|
|
toggle: () => void;
|
|
};
|
|
|
|
const ThemeContext = createContext<ThemeContextValue>();
|
|
|
|
export const ThemeProvider: ParentComponent = (props) => {
|
|
const [theme, setThemeSignal] = createSignal<Theme>(getInitialTheme());
|
|
const [resolvedTheme, setResolvedTheme] = createSignal<"light" | "dark">(
|
|
getResolvedTheme(getInitialTheme())
|
|
);
|
|
|
|
const applyTheme = (t: Theme) => {
|
|
const resolved = getResolvedTheme(t);
|
|
setResolvedTheme(resolved);
|
|
|
|
const root = document.documentElement;
|
|
root.classList.remove("light", "dark");
|
|
root.classList.add(resolved);
|
|
root.setAttribute("data-theme", resolved);
|
|
};
|
|
|
|
createEffect(() => {
|
|
const t = theme();
|
|
localStorage.setItem(STORAGE_KEY, t);
|
|
applyTheme(t);
|
|
});
|
|
|
|
if (typeof window !== "undefined") {
|
|
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
mediaQuery.addEventListener("change", () => {
|
|
if (theme() === "system") {
|
|
applyTheme("system");
|
|
}
|
|
});
|
|
}
|
|
|
|
const setTheme = (t: Theme) => setThemeSignal(t);
|
|
|
|
const toggle = () => {
|
|
const current = resolvedTheme();
|
|
setTheme(current === "light" ? "dark" : "light");
|
|
};
|
|
|
|
return (
|
|
<ThemeContext.Provider value={{ theme, resolvedTheme, setTheme, toggle }}>
|
|
{props.children}
|
|
</ThemeContext.Provider>
|
|
);
|
|
};
|
|
|
|
export function useTheme() {
|
|
const context = useContext(ThemeContext);
|
|
if (!context) {
|
|
throw new Error("ThemeProvider is missing from the component tree.");
|
|
}
|
|
return context;
|
|
}
|