Next.js 15 App Router mit öffentlicher Übersicht und geschütztem Admin-Bereich zur Pflege von Datenschutzerklärungen nach Art. 12ff. DSGVO. - Oberfläche auf Basis von shadcn/ui, Inter lokal eingebunden - Prisma/SQLite, Auth.js mit Credentials-Provider und Rollen - Massenimport der Merkblätter des Amtes Leezen aus PDF - Docker-Image (standalone) und Gitea-Workflow zur Veröffentlichung Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
60 lines
1.5 KiB
TypeScript
60 lines
1.5 KiB
TypeScript
import { useEffect, useState } from 'react'
|
|
|
|
type Theme = 'dark' | 'light' | 'system'
|
|
|
|
export function useTheme() {
|
|
const [theme, setTheme] = useState<Theme>('system')
|
|
const [resolvedTheme, setResolvedTheme] = useState<'dark' | 'light'>('light')
|
|
|
|
useEffect(() => {
|
|
const stored = localStorage.getItem('theme') as Theme | null
|
|
if (stored) {
|
|
setTheme(stored)
|
|
}
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
const root = window.document.documentElement
|
|
|
|
// Remove existing theme classes
|
|
root.classList.remove('light', 'dark')
|
|
|
|
if (theme === 'system') {
|
|
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
? 'dark'
|
|
: 'light'
|
|
|
|
root.classList.add(systemTheme)
|
|
setResolvedTheme(systemTheme)
|
|
} else {
|
|
root.classList.add(theme)
|
|
setResolvedTheme(theme)
|
|
}
|
|
|
|
// Store theme preference
|
|
localStorage.setItem('theme', theme)
|
|
}, [theme])
|
|
|
|
// Listen for system theme changes
|
|
useEffect(() => {
|
|
if (theme !== 'system') return
|
|
|
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
|
|
|
const handleChange = () => {
|
|
const systemTheme = mediaQuery.matches ? 'dark' : 'light'
|
|
document.documentElement.classList.remove('light', 'dark')
|
|
document.documentElement.classList.add(systemTheme)
|
|
setResolvedTheme(systemTheme)
|
|
}
|
|
|
|
mediaQuery.addEventListener('change', handleChange)
|
|
return () => mediaQuery.removeEventListener('change', handleChange)
|
|
}, [theme])
|
|
|
|
return {
|
|
theme,
|
|
setTheme,
|
|
resolvedTheme,
|
|
}
|
|
} |