60 lines
1.7 KiB
TypeScript
60 lines
1.7 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
|
|
const STORAGE_KEY = 'magent_theme'
|
|
|
|
const getPreferredTheme = () => {
|
|
if (typeof window === 'undefined') return 'dark'
|
|
const stored = window.localStorage.getItem(STORAGE_KEY)
|
|
if (stored === 'light' || stored === 'dark') {
|
|
return stored
|
|
}
|
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
|
}
|
|
|
|
const applyTheme = (theme: string) => {
|
|
if (typeof document === 'undefined') return
|
|
document.documentElement.setAttribute('data-theme', theme)
|
|
}
|
|
|
|
export default function ThemeToggle() {
|
|
const [theme, setTheme] = useState<'light' | 'dark'>('dark')
|
|
|
|
useEffect(() => {
|
|
const preferred = getPreferredTheme()
|
|
setTheme(preferred)
|
|
applyTheme(preferred)
|
|
}, [])
|
|
|
|
const toggle = () => {
|
|
const next = theme === 'dark' ? 'light' : 'dark'
|
|
setTheme(next)
|
|
applyTheme(next)
|
|
if (typeof window !== 'undefined') {
|
|
window.localStorage.setItem(STORAGE_KEY, next)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<button
|
|
type="button"
|
|
className="theme-toggle"
|
|
onClick={toggle}
|
|
aria-label={theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}
|
|
title={theme === 'dark' ? 'Light mode' : 'Dark mode'}
|
|
>
|
|
{theme === 'dark' ? (
|
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
<circle cx="12" cy="12" r="4" />
|
|
<path d="M12 2v3M12 19v3M4.22 4.22l2.12 2.12M17.66 17.66l2.12 2.12M2 12h3M19 12h3M4.22 19.78l2.12-2.12M17.66 6.34l2.12-2.12" />
|
|
</svg>
|
|
) : (
|
|
<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
<path d="M21 14.5A8.5 8.5 0 0 1 9.5 3a8.5 8.5 0 1 0 11.5 11.5z" />
|
|
</svg>
|
|
)}
|
|
</button>
|
|
)
|
|
}
|