| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283 |
- import { createContext, useContext, useEffect, useState } from "react";
- type Theme = "dark" | "light" | "system";
- type ThemeProviderProps = {
- children: React.ReactNode;
- defaultTheme?: Theme;
- storageKey?: string;
- };
- type ThemeProviderState = {
- theme: Theme;
- setTheme: (theme: Theme) => void;
- };
- const initialState: ThemeProviderState = {
- theme: "system",
- setTheme: () => null,
- };
- const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
- const getThemeFromCookie = (key: string): Theme | null => {
- const match = document.cookie.match(new RegExp(`(^| )${key}=([^;]+)`));
- if (match && (match[2] === "dark" || match[2] === "light" || match[2] === "system")) {
- return match[2] as Theme;
- }
- return null;
- };
- export function ThemeProvider({
- children,
- defaultTheme = "system",
- storageKey = "vite-ui-theme",
- ...props
- }: ThemeProviderProps) {
-
- const [theme, setTheme] = useState<Theme>(() => {
- const cookieTheme = getThemeFromCookie(storageKey);
- if (cookieTheme) return cookieTheme;
-
- return (localStorage.getItem(storageKey) as Theme) || defaultTheme;
- });
- useEffect(() => {
- const root = window.document.documentElement;
- root.classList.remove("light", "dark");
- let activeTheme = theme;
- if (theme === "system") {
- activeTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
- }
- root.classList.add(activeTheme);
-
- localStorage.setItem(storageKey, theme);
- document.cookie = `${storageKey}=${activeTheme}; path=/; max-age=31536000; SameSite=Lax`;
-
- }, [theme, storageKey]);
- const value = {
- theme,
- setTheme: (newTheme: Theme) => {
- setTheme(newTheme);
- },
- };
- return (
- <ThemeProviderContext.Provider {...props} value={value}>
- {children}
- </ThemeProviderContext.Provider>
- );
- }
- export const useTheme = () => {
- const context = useContext(ThemeProviderContext);
- if (context === undefined)
- throw new Error("useTheme must be used within a ThemeProvider");
- return context;
- };
|