react-vite-themes
Version:
A test/experimental React theme system created for learning purposes. Features atomic design components, SCSS variables, and dark/light theme support. Not intended for production use.
90 lines (89 loc) • 3.21 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
import React, { createContext, useContext, useState, useEffect } from 'react';
import { useLocalStorage } from '../hooks/useLocalStorage';
const ThemeContext = createContext(undefined);
export const ThemeProvider = ({ children }) => {
// Get system theme preference
const getSystemTheme = () => {
if (typeof window !== 'undefined') {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
return 'light'; // Default fallback
};
// Initialize theme from localStorage or system preference
const getInitialTheme = () => {
if (typeof window !== 'undefined') {
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'light' || savedTheme === 'dark') {
return savedTheme;
}
// If no saved preference, use system theme
return getSystemTheme();
}
return 'light';
};
const [theme, setTheme] = useLocalStorage('theme', getInitialTheme());
const [isSystemTheme, setIsSystemTheme] = useState(() => {
if (typeof window !== 'undefined') {
return !localStorage.getItem('theme'); // true if no saved preference
}
return true;
});
// Listen for system theme changes
useEffect(() => {
if (typeof window === 'undefined')
return;
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const handleSystemThemeChange = (e) => {
if (isSystemTheme) {
const newTheme = e.matches ? 'dark' : 'light';
setTheme(newTheme);
}
};
mediaQuery.addEventListener('change', handleSystemThemeChange);
return () => {
mediaQuery.removeEventListener('change', handleSystemThemeChange);
};
}, [isSystemTheme, setTheme]);
// Apply theme to document
useEffect(() => {
if (typeof document !== 'undefined') {
document.documentElement.setAttribute('data-theme', theme);
}
}, [theme]);
const toggleTheme = () => {
const newTheme = theme === 'light' ? 'dark' : 'light';
setTheme(newTheme);
setIsSystemTheme(false); // User manually changed theme
};
const setSystemTheme = () => {
const systemTheme = getSystemTheme();
setTheme(systemTheme);
setIsSystemTheme(true);
localStorage.removeItem('theme'); // Clear saved preference
};
const setLightTheme = () => {
setTheme('light');
setIsSystemTheme(false);
};
const setDarkTheme = () => {
setTheme('dark');
setIsSystemTheme(false);
};
const value = {
theme,
isSystemTheme,
toggleTheme,
setSystemTheme,
setLightTheme,
setDarkTheme,
};
return (_jsx(ThemeContext.Provider, { value: value, children: children }));
};
export const useTheme = () => {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
};