All files / src/contexts ThemeContext.tsx

28.57% Statements 10/35
100% Branches 1/1
25% Functions 1/4
28.57% Lines 10/35

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 531x                 1x   1x 1x 1x 1x                             1x                                       1x 6x 6x  
import { createContext, useContext, useEffect, useMemo, useState, type ReactNode } from 'react';
 
type Theme = 'light' | 'dark';
 
type ThemeContextValue = {
  theme: Theme;
  toggleTheme: () => void;
};
 
const STORAGE_KEY_THEME = 'sem-theme';
 
const ThemeContext = createContext<ThemeContextValue>({
  theme: 'light',
  toggleTheme: () => undefined,
});
 
function getInitialTheme(): Theme {
  if (typeof window === 'undefined') {
    return 'light';
  }
 
  const storedTheme = window.localStorage.getItem(STORAGE_KEY_THEME);
  if (storedTheme === 'light' || storedTheme === 'dark') {
    return storedTheme;
  }
 
  return 'light';
}
 
export function ThemeProvider({ children }: { children: ReactNode }) {
  const [theme, setTheme] = useState<Theme>(getInitialTheme);
 
  useEffect(() => {
    document.documentElement.dataset.theme = theme;
    document.documentElement.style.colorScheme = theme;
    window.localStorage.setItem(STORAGE_KEY_THEME, theme);
  }, [theme]);
 
  const value = useMemo(
    () => ({
      theme,
      toggleTheme: () => setTheme((current) => (current === 'light' ? 'dark' : 'light')),
    }),
    [theme],
  );
 
  return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
}
 
export function useTheme() {
  return useContext(ThemeContext);
}