Skip to content

Commit e22a58b

Browse files
committed
light/dark mode selector
1 parent 1e4fd68 commit e22a58b

File tree

2 files changed

+36
-24
lines changed

2 files changed

+36
-24
lines changed

frontend/src/context/ThemeContext.tsx

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,38 @@ interface ThemeProviderProps {
1414
children: ReactNode;
1515
}
1616

17-
export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
18-
const [theme, setThemeState] = useState<Theme>('system');
19-
const [effectiveTheme, setEffectiveTheme] = useState<'light' | 'dark'>('light');
20-
21-
// Initialize theme from localStorage or default to system
22-
useEffect(() => {
17+
// Initialize theme from localStorage before component render
18+
const getInitialTheme = (): Theme => {
19+
if (typeof window !== 'undefined') {
2320
const savedTheme = localStorage.getItem('theme') as Theme | null;
2421
if (savedTheme && ['light', 'dark', 'system'].includes(savedTheme)) {
25-
setThemeState(savedTheme);
22+
return savedTheme;
2623
}
27-
}, []);
24+
}
25+
return 'system';
26+
};
2827

29-
// Update effective theme based on theme setting and system preference
30-
useEffect(() => {
31-
const updateEffectiveTheme = () => {
32-
if (theme === 'system') {
33-
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
34-
setEffectiveTheme(systemPrefersDark ? 'dark' : 'light');
35-
} else {
36-
setEffectiveTheme(theme);
37-
}
38-
};
28+
// Get effective theme based on theme setting
29+
const getEffectiveTheme = (theme: Theme): 'light' | 'dark' => {
30+
if (theme === 'system') {
31+
if (typeof window !== 'undefined') {
32+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
33+
}
34+
return 'light';
35+
}
36+
return theme;
37+
};
3938

40-
updateEffectiveTheme();
39+
export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
40+
const [theme, setThemeState] = useState<Theme>(getInitialTheme);
41+
const [effectiveTheme, setEffectiveTheme] = useState<'light' | 'dark'>(
42+
getEffectiveTheme(getInitialTheme())
43+
);
44+
45+
// Update effective theme when theme changes
46+
useEffect(() => {
47+
const newEffectiveTheme = getEffectiveTheme(theme);
48+
setEffectiveTheme(newEffectiveTheme);
4149

4250
// Listen for system preference changes when in system mode
4351
if (theme === 'system') {
@@ -54,11 +62,17 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
5462
// Apply theme to document
5563
useEffect(() => {
5664
const root = document.documentElement;
65+
5766
if (effectiveTheme === 'dark') {
5867
root.classList.add('dark');
5968
} else {
6069
root.classList.remove('dark');
6170
}
71+
72+
// Also apply to body for background
73+
document.body.className = effectiveTheme === 'dark'
74+
? 'bg-gray-900 text-gray-50'
75+
: 'bg-gray-50 text-gray-900';
6276
}, [effectiveTheme]);
6377

6478
const setTheme = (newTheme: Theme) => {

frontend/src/styles/globals.css

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
@import "tailwindcss";
22

33
@layer base {
4-
body {
4+
:root {
55
@apply bg-gray-50 text-gray-900;
66
}
77

8-
@media (prefers-color-scheme: dark) {
9-
body {
10-
@apply bg-gray-900 text-gray-50;
11-
}
8+
:root.dark {
9+
@apply bg-gray-900 text-gray-50;
1210
}
1311
}

0 commit comments

Comments
 (0)