Implement theme toggle functionality and refactor styles for light/dark mode support

This commit is contained in:
2025-10-03 12:00:38 +08:00
parent 765ef6f9fc
commit 7597e8db33
8 changed files with 290 additions and 76 deletions

View File

@@ -0,0 +1,68 @@
import React, { createContext, useState, useEffect } from 'react'
import type { ReactNode } from 'react'
type Theme = 'light' | 'dark'
interface ThemeContextType {
theme: Theme
toggleTheme: () => void
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined)
export { ThemeContext }
interface ThemeProviderProps {
children: ReactNode
}
export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
// Get initial theme from localStorage or system preference
const getInitialTheme = (): Theme => {
const savedTheme = localStorage.getItem('theme') as Theme
if (savedTheme) {
return savedTheme
}
// Check system preference
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
const [theme, setTheme] = useState<Theme>(getInitialTheme)
// Update document class and localStorage when theme changes
useEffect(() => {
const root = document.documentElement
root.classList.remove('light', 'dark')
root.classList.add(theme)
localStorage.setItem('theme', theme)
}, [theme])
// Listen for system theme changes
useEffect(() => {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
const handleChange = (e: MediaQueryListEvent) => {
// Only update if no manual preference is saved
if (!localStorage.getItem('theme')) {
setTheme(e.matches ? 'dark' : 'light')
}
}
mediaQuery.addEventListener('change', handleChange)
return () => mediaQuery.removeEventListener('change', handleChange)
}, [])
const toggleTheme = () => {
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light')
}
const value = {
theme,
toggleTheme
}
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
)
}