From e922cd00b4c22eb875b3a0bc71261604f49ab9b4 Mon Sep 17 00:00:00 2001 From: oraimoitel <272064339+oraimoitel@users.noreply.github.com> Date: Sun, 30 Aug 2026 12:29:32 +0200 Subject: [PATCH 1/3] feat(dashboard): add dark mode support with theme toggle Closes #117 - Add ThemeProvider with localStorage persistence - Add ThemeToggle component (sun/moon icons) in TopBar - Configure Tailwind darkMode: 'class' - Add dark: variants to layout, sidebar, globals, and badges - Accessible toggle with aria-label and keyboard support --- apps/dashboard/src/app/globals.css | 18 +++---- apps/dashboard/src/app/layout.tsx | 35 +++++++------ .../src/components/layout/Sidebar.tsx | 14 ++--- .../src/components/layout/TopBar.tsx | 12 +++-- .../src/components/theme/ThemeProvider.tsx | 52 +++++++++++++++++++ .../src/components/theme/ThemeToggle.tsx | 37 +++++++++++++ apps/dashboard/tailwind.config.ts | 1 + 7 files changed, 133 insertions(+), 36 deletions(-) create mode 100644 apps/dashboard/src/components/theme/ThemeProvider.tsx create mode 100644 apps/dashboard/src/components/theme/ThemeToggle.tsx diff --git a/apps/dashboard/src/app/globals.css b/apps/dashboard/src/app/globals.css index 9c7906c..80f7324 100644 --- a/apps/dashboard/src/app/globals.css +++ b/apps/dashboard/src/app/globals.css @@ -9,7 +9,7 @@ } body { - @apply bg-gray-950 text-gray-100; + @apply bg-white text-gray-900 dark:bg-gray-950 dark:text-gray-100; } /* Scrollbar styling */ @@ -17,19 +17,19 @@ @apply w-1.5 h-1.5; } ::-webkit-scrollbar-track { - @apply bg-gray-900; + @apply bg-gray-100 dark:bg-gray-900; } ::-webkit-scrollbar-thumb { - @apply bg-gray-700 rounded-full; + @apply bg-gray-300 dark:bg-gray-700 rounded-full; } ::-webkit-scrollbar-thumb:hover { - @apply bg-gray-600; + @apply bg-gray-400 dark:bg-gray-600; } } @layer components { .sentinel-card { - @apply bg-gray-900 border border-gray-800 rounded-xl p-6; + @apply bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-800 rounded-xl p-6; } .sentinel-badge { @@ -37,18 +37,18 @@ } .sentinel-badge--critical { - @apply bg-red-950 text-red-400 border border-red-800; + @apply bg-red-50 text-red-700 border border-red-200 dark:bg-red-950 dark:text-red-400 dark:border-red-800; } .sentinel-badge--high { - @apply bg-orange-950 text-orange-400 border border-orange-800; + @apply bg-orange-50 text-orange-700 border border-orange-200 dark:bg-orange-950 dark:text-orange-400 dark:border-orange-800; } .sentinel-badge--medium { - @apply bg-yellow-950 text-yellow-400 border border-yellow-800; + @apply bg-yellow-50 text-yellow-700 border border-yellow-200 dark:bg-yellow-950 dark:text-yellow-400 dark:border-yellow-800; } .sentinel-badge--low { - @apply bg-blue-950 text-blue-400 border border-blue-800; + @apply bg-blue-50 text-blue-700 border border-blue-200 dark:bg-blue-950 dark:text-blue-400 dark:border-blue-800; } } diff --git a/apps/dashboard/src/app/layout.tsx b/apps/dashboard/src/app/layout.tsx index c743374..f46a7f8 100644 --- a/apps/dashboard/src/app/layout.tsx +++ b/apps/dashboard/src/app/layout.tsx @@ -2,6 +2,7 @@ import type { Metadata } from 'next'; import './globals.css'; import { Sidebar } from '@/components/layout/Sidebar'; import { TopBar } from '@/components/layout/TopBar'; +import { ThemeProvider } from '@/components/theme/ThemeProvider'; export const metadata: Metadata = { title: { @@ -20,23 +21,25 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - - - {/* Sidebar */} - + + + + {/* Sidebar */} + - {/* Main content area */} -
- -
- {children} -
-
+ {/* Main content area */} +
+ +
+ {children} +
+
+
); diff --git a/apps/dashboard/src/components/layout/Sidebar.tsx b/apps/dashboard/src/components/layout/Sidebar.tsx index 735b080..be1d70a 100644 --- a/apps/dashboard/src/components/layout/Sidebar.tsx +++ b/apps/dashboard/src/components/layout/Sidebar.tsx @@ -143,11 +143,11 @@ export function Sidebar() { return ( ); diff --git a/apps/dashboard/src/components/layout/TopBar.tsx b/apps/dashboard/src/components/layout/TopBar.tsx index 8d0d89b..af041db 100644 --- a/apps/dashboard/src/components/layout/TopBar.tsx +++ b/apps/dashboard/src/components/layout/TopBar.tsx @@ -1,6 +1,7 @@ 'use client'; import { usePathname } from 'next/navigation'; +import { ThemeToggle } from '@/components/theme/ThemeToggle'; const pageTitles: Record = { '/': 'Overview', @@ -15,12 +16,12 @@ export function TopBar() { const title = pageTitles[pathname] ?? 'Dashboard'; return ( -
+
{/* Breadcrumb / page title */} -
- Sentinel +
+ Sentinel + {/* Theme toggle */} + + {/* Live indicator */}
void; +} + +const ThemeContext = createContext(undefined); + +export function useTheme() { + const ctx = useContext(ThemeContext); + if (!ctx) throw new Error('useTheme must be used within ThemeProvider'); + return ctx; +} + +export function ThemeProvider({ children }: { children: ReactNode }) { + const [theme, setTheme] = useState('dark'); + const [mounted, setMounted] = useState(false); + + useEffect(() => { + const stored = localStorage.getItem('sentinel-theme') as Theme | null; + const initial = stored ?? 'dark'; + setTheme(initial); + document.documentElement.classList.toggle('dark', initial === 'dark'); + document.documentElement.classList.toggle('light', initial === 'light'); + setMounted(true); + }, []); + + const toggleTheme = () => { + setTheme((prev) => { + const next = prev === 'dark' ? 'light' : 'dark'; + localStorage.setItem('sentinel-theme', next); + document.documentElement.classList.remove('dark', 'light'); + document.documentElement.classList.add(next); + return next; + }); + }; + + if (!mounted) { + return <>{children}; + } + + return ( + + {children} + + ); +} diff --git a/apps/dashboard/src/components/theme/ThemeToggle.tsx b/apps/dashboard/src/components/theme/ThemeToggle.tsx new file mode 100644 index 0000000..ca39609 --- /dev/null +++ b/apps/dashboard/src/components/theme/ThemeToggle.tsx @@ -0,0 +1,37 @@ +'use client'; + +import { useTheme } from './ThemeProvider'; + +export function ThemeToggle() { + const { theme, toggleTheme } = useTheme(); + + return ( + + ); +} diff --git a/apps/dashboard/tailwind.config.ts b/apps/dashboard/tailwind.config.ts index 9329fd2..bbc9adf 100644 --- a/apps/dashboard/tailwind.config.ts +++ b/apps/dashboard/tailwind.config.ts @@ -1,6 +1,7 @@ import type { Config } from 'tailwindcss'; const config: Config = { + darkMode: 'class', content: [ './src/pages/**/*.{js,ts,jsx,tsx,mdx}', './src/components/**/*.{js,ts,jsx,tsx,mdx}', From ad8fe156559308fc52aa693b938c4a9e3954e7d1 Mon Sep 17 00:00:00 2001 From: oraimoitel <272064339+oraimoitel@users.noreply.github.com> Date: Sun, 30 Aug 2026 14:34:58 +0200 Subject: [PATCH 2/3] style(dashboard): apply prettier formatting to pass ci lint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with Codebuff Co-Authored-By: Codebuff --- apps/dashboard/src/components/layout/Sidebar.tsx | 4 +++- .../src/components/theme/ThemeProvider.tsx | 8 ++------ .../src/components/theme/ThemeToggle.tsx | 16 ++++++++++++++-- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/apps/dashboard/src/components/layout/Sidebar.tsx b/apps/dashboard/src/components/layout/Sidebar.tsx index be1d70a..734fb8b 100644 --- a/apps/dashboard/src/components/layout/Sidebar.tsx +++ b/apps/dashboard/src/components/layout/Sidebar.tsx @@ -165,7 +165,9 @@ export function Sidebar() { />
- Sentinel + + Sentinel +
diff --git a/apps/dashboard/src/components/theme/ThemeProvider.tsx b/apps/dashboard/src/components/theme/ThemeProvider.tsx index 145eaa0..e9ce4ff 100644 --- a/apps/dashboard/src/components/theme/ThemeProvider.tsx +++ b/apps/dashboard/src/components/theme/ThemeProvider.tsx @@ -31,7 +31,7 @@ export function ThemeProvider({ children }: { children: ReactNode }) { }, []); const toggleTheme = () => { - setTheme((prev) => { + setTheme(prev => { const next = prev === 'dark' ? 'light' : 'dark'; localStorage.setItem('sentinel-theme', next); document.documentElement.classList.remove('dark', 'light'); @@ -44,9 +44,5 @@ export function ThemeProvider({ children }: { children: ReactNode }) { return <>{children}; } - return ( - - {children} - - ); + return {children}; } diff --git a/apps/dashboard/src/components/theme/ThemeToggle.tsx b/apps/dashboard/src/components/theme/ThemeToggle.tsx index ca39609..7527f7a 100644 --- a/apps/dashboard/src/components/theme/ThemeToggle.tsx +++ b/apps/dashboard/src/components/theme/ThemeToggle.tsx @@ -14,7 +14,13 @@ export function ThemeToggle() { title={`Switch to ${theme === 'dark' ? 'light' : 'dark'} mode`} > {theme === 'dark' ? ( -