diff --git a/ui/app/layout.tsx b/ui/app/layout.tsx index b3f5005..87144cf 100644 --- a/ui/app/layout.tsx +++ b/ui/app/layout.tsx @@ -4,6 +4,7 @@ import './globals.css'; import { cn } from '@/lib/utils'; import Sidebar from '@/components/Sidebar'; import { Toaster } from 'sonner'; +import { ThemeProviderComponent } from '@/components/theme/Provider'; const montserrat = Montserrat({ weight: ['300', '400', '500', '700'], @@ -24,18 +25,20 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - + - {children} - + + {children} + + ); diff --git a/ui/components/theme/Provider.tsx b/ui/components/theme/Provider.tsx new file mode 100644 index 0000000..2e110f6 --- /dev/null +++ b/ui/components/theme/Provider.tsx @@ -0,0 +1,14 @@ +'use client'; +import { ThemeProvider } from 'next-themes'; + +export function ThemeProviderComponent({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/ui/components/theme/Switcher.tsx b/ui/components/theme/Switcher.tsx new file mode 100644 index 0000000..d1f44a3 --- /dev/null +++ b/ui/components/theme/Switcher.tsx @@ -0,0 +1,63 @@ +'use client'; +import { useTheme } from 'next-themes'; +import { SunIcon, MoonIcon, MonitorIcon } from 'lucide-react'; +import { useCallback, useEffect, useState } from 'react'; + +type Theme = 'dark' | 'light' | 'system'; + +export function ThemeSwitcher() { + const [mounted, setMounted] = useState(false); + + const { theme, setTheme } = useTheme(); + + const isTheme = useCallback((t: Theme) => t === theme, [theme]); + + const handleThemeSwitch = (theme: Theme) => { + setTheme(theme); + }; + + useEffect(() => { + setMounted(true); + }, []); + + useEffect(() => { + if (isTheme('system')) { + const preferDarkScheme = window.matchMedia( + '(prefers-color-scheme: dark)', + ); + + const detectThemeChange = (event: MediaQueryListEvent) => { + const theme: Theme = event.matches ? 'dark' : 'light'; + setTheme(theme); + }; + + preferDarkScheme.addEventListener('change', detectThemeChange); + + return () => { + preferDarkScheme.removeEventListener('change', detectThemeChange); + }; + } + }, [isTheme, setTheme, theme]); + + // Avoid Hydration Mismatch + if (!mounted) { + return null; + } + + return isTheme('dark') ? ( + handleThemeSwitch('light')} + /> + ) : isTheme('light') ? ( + handleThemeSwitch('dark')} + /> + ) : ( + handleThemeSwitch('system')} + /> + ); +} diff --git a/ui/tailwind.config.ts b/ui/tailwind.config.ts index 05f107d..a757263 100644 --- a/ui/tailwind.config.ts +++ b/ui/tailwind.config.ts @@ -6,6 +6,7 @@ const config: Config = { './components/**/*.{js,ts,jsx,tsx,mdx}', './app/**/*.{js,ts,jsx,tsx,mdx}', ], + darkMode: 'class', theme: { extend: {}, },