From 46f88ca04cb108fa496f460d9d8ea6e0545ebe61 Mon Sep 17 00:00:00 2001 From: Shihaam Abdul Rahman Date: Fri, 31 Jul 2026 23:15:33 +0500 Subject: [PATCH] add dark theme --- index.html | 14 ++++++++++++++ src/App.tsx | 28 ++++++++++++++++++++++++---- src/lib/theme.tsx | 38 ++++++++++++++++++++++++++++++++++++++ src/main.tsx | 15 +++++++++------ 4 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 src/lib/theme.tsx diff --git a/index.html b/index.html index 427d1f2..1267038 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,20 @@ RADIUS Admin + + +
diff --git a/src/App.tsx b/src/App.tsx index d4204bc..8cb108a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,12 +1,29 @@ import { NavLink, Navigate, Route, Routes } from 'react-router-dom' -import { LogOut, Router, Wifi } from 'lucide-react' +import { LogOut, Moon, Router, Sun, Wifi } from 'lucide-react' import { useAuth } from '@/auth/auth' +import { useTheme } from '@/lib/theme' import { Login } from '@/pages/Login' import { Devices } from '@/pages/Devices' import { Vlans } from '@/pages/Vlans' import { Button } from '@/components/ui/button' import { cn } from '@/lib/utils' +function ThemeToggle() { + const { theme, toggle } = useTheme() + return ( + + ) +} + function Nav() { const { logout } = useAuth() const linkClass = ({ isActive }: { isActive: boolean }) => @@ -28,9 +45,12 @@ function Nav() { VLANs - +
+ + +
) diff --git a/src/lib/theme.tsx b/src/lib/theme.tsx new file mode 100644 index 0000000..9007117 --- /dev/null +++ b/src/lib/theme.tsx @@ -0,0 +1,38 @@ +import { createContext, useCallback, useContext, useEffect, useState, type ReactNode } from 'react' + +type Theme = 'light' | 'dark' +const STORAGE = 'radui.theme' + +interface ThemeState { + theme: Theme + toggle: () => void +} + +const ThemeContext = createContext(null) + +function initialTheme(): Theme { + const saved = localStorage.getItem(STORAGE) + if (saved === 'light' || saved === 'dark') return saved + return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' +} + +export function ThemeProvider({ children }: { children: ReactNode }) { + const [theme, setTheme] = useState(initialTheme) + + useEffect(() => { + document.documentElement.classList.toggle('dark', theme === 'dark') + localStorage.setItem(STORAGE, theme) + }, [theme]) + + const toggle = useCallback(() => { + setTheme((t) => (t === 'dark' ? 'light' : 'dark')) + }, []) + + return {children} +} + +export function useTheme(): ThemeState { + const ctx = useContext(ThemeContext) + if (!ctx) throw new Error('useTheme must be used within ThemeProvider') + return ctx +} diff --git a/src/main.tsx b/src/main.tsx index 6b0375b..f24acae 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -4,15 +4,18 @@ import { BrowserRouter } from 'react-router-dom' import './index.css' import App from './App.tsx' import { AuthProvider } from './auth/auth.tsx' +import { ThemeProvider } from './lib/theme.tsx' import { Toaster } from './components/ui/sonner.tsx' createRoot(document.getElementById('root')!).render( - - - - - - + + + + + + + + , )