feat: dark mode with theme toggle (light/dark/system)
This commit is contained in:
57
src/hooks/useTheme.ts
Normal file
57
src/hooks/useTheme.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
|
||||
type Theme = 'light' | 'dark' | 'system';
|
||||
|
||||
function getSystemTheme(): 'light' | 'dark' {
|
||||
if (typeof window === 'undefined') return 'light';
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
function applyTheme(theme: Theme) {
|
||||
const resolved = theme === 'system' ? getSystemTheme() : theme;
|
||||
const root = document.documentElement;
|
||||
if (resolved === 'dark') {
|
||||
root.classList.add('dark');
|
||||
} else {
|
||||
root.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
|
||||
export function useTheme() {
|
||||
const [theme, setThemeState] = useState<Theme>('system');
|
||||
|
||||
// Read from localStorage on mount
|
||||
useEffect(() => {
|
||||
const stored = localStorage.getItem('simo-theme') as Theme | null;
|
||||
const initial = stored ?? 'system';
|
||||
setThemeState(initial);
|
||||
applyTheme(initial);
|
||||
}, []);
|
||||
|
||||
// Listen for system theme changes when in 'system' mode
|
||||
useEffect(() => {
|
||||
const mq = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
const handler = () => {
|
||||
if (theme === 'system') applyTheme('system');
|
||||
};
|
||||
mq.addEventListener('change', handler);
|
||||
return () => mq.removeEventListener('change', handler);
|
||||
}, [theme]);
|
||||
|
||||
const setTheme = useCallback((t: Theme) => {
|
||||
setThemeState(t);
|
||||
localStorage.setItem('simo-theme', t);
|
||||
applyTheme(t);
|
||||
}, []);
|
||||
|
||||
const cycle = useCallback(() => {
|
||||
const order: Theme[] = ['light', 'dark', 'system'];
|
||||
const idx = order.indexOf(theme);
|
||||
const next = order[(idx + 1) % order.length];
|
||||
setTheme(next);
|
||||
}, [theme, setTheme]);
|
||||
|
||||
return { theme, setTheme, cycle };
|
||||
}
|
||||
Reference in New Issue
Block a user