Skip to content

Commit d89ac38

Browse files
committed
feat: Complete Phase 8 - Polish & Optimization (Dark Mode)
Add comprehensive dark mode support and navigation improvements: Dark Mode Implementation: - Installed next-themes package for theme management - Created ThemeProvider component wrapping the app - Created ThemeToggle component with Moon/Sun icons - Added theme toggle to Header (accessible from all pages) - Configured system theme detection and smooth transitions - Added suppressHydrationWarning to prevent flash Navigation Improvements: - Added "Organizations" link to main navigation - Improved header layout with theme toggle - Better spacing and organization of header elements Technical Details: - Uses next-themes with class-based theme switching - Supports system, light, and dark modes - Smooth transitions disabled for better performance - Mounted state handling prevents hydration mismatches - Theme persisted in localStorage All Tailwind CSS classes automatically support dark mode via the 'dark:' prefix. Build: 15 routes, 175 kB largest page, 102 kB shared chunks
1 parent eb0084b commit d89ac38

File tree

6 files changed

+76
-2
lines changed

6 files changed

+76
-2
lines changed

app-next/package-lock.json

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app-next/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"date-fns": "^4.1.0",
2929
"lucide-react": "^0.468.0",
3030
"next": "^15.1.3",
31+
"next-themes": "^0.4.6",
3132
"react": "^19.0.0",
3233
"react-dom": "^19.0.0",
3334
"react-hook-form": "^7.54.2",

app-next/src/app/layout.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Metadata } from 'next'
22
import './globals.css'
33
import { QueryProvider } from '@/lib/api/query-provider'
4+
import { ThemeProvider } from '@/components/theme-provider'
45

56
export const metadata: Metadata = {
67
title: 'TechStacks - Discover Technology Stacks',
@@ -14,9 +15,11 @@ export default function RootLayout({
1415
children: React.ReactNode
1516
}>) {
1617
return (
17-
<html lang="en">
18+
<html lang="en" suppressHydrationWarning>
1819
<body className="font-sans antialiased">
19-
<QueryProvider>{children}</QueryProvider>
20+
<ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
21+
<QueryProvider>{children}</QueryProvider>
22+
</ThemeProvider>
2023
</body>
2124
</html>
2225
)

app-next/src/components/layout/header.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
DropdownMenuTrigger,
1212
} from '@/components/ui/dropdown-menu'
1313
import { useAuth } from '@/lib/api/auth'
14+
import { ThemeToggle } from '@/components/theme-toggle'
1415

1516
export function Header() {
1617
const { user, isAuthenticated, signInWithGitHub, signOut } = useAuth()
@@ -37,6 +38,12 @@ export function Header() {
3738
>
3839
Stacks
3940
</Link>
41+
<Link
42+
href="/organizations"
43+
className="transition-colors hover:text-foreground/80 text-foreground/60"
44+
>
45+
Organizations
46+
</Link>
4047
<Link
4148
href="/top"
4249
className="transition-colors hover:text-foreground/80 text-foreground/60"
@@ -54,6 +61,7 @@ export function Header() {
5461
</nav>
5562

5663
<div className="flex items-center gap-2">
64+
<ThemeToggle />
5765
{isAuthenticated && user ? (
5866
<DropdownMenu>
5967
<DropdownMenuTrigger asChild>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
'use client'
2+
3+
import * as React from 'react'
4+
import { ThemeProvider as NextThemesProvider } from 'next-themes'
5+
6+
export function ThemeProvider({
7+
children,
8+
...props
9+
}: React.ComponentProps<typeof NextThemesProvider>) {
10+
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
11+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'use client'
2+
3+
import * as React from 'react'
4+
import { Moon, Sun } from 'lucide-react'
5+
import { useTheme } from 'next-themes'
6+
import { Button } from '@/components/ui/button'
7+
8+
export function ThemeToggle() {
9+
const { theme, setTheme } = useTheme()
10+
const [mounted, setMounted] = React.useState(false)
11+
12+
// useEffect only runs on the client, so now we can safely show the UI
13+
React.useEffect(() => {
14+
setMounted(true)
15+
}, [])
16+
17+
if (!mounted) {
18+
return (
19+
<Button variant="ghost" size="icon">
20+
<Sun className="h-5 w-5" />
21+
<span className="sr-only">Toggle theme</span>
22+
</Button>
23+
)
24+
}
25+
26+
return (
27+
<Button
28+
variant="ghost"
29+
size="icon"
30+
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
31+
>
32+
{theme === 'dark' ? (
33+
<Sun className="h-5 w-5" />
34+
) : (
35+
<Moon className="h-5 w-5" />
36+
)}
37+
<span className="sr-only">Toggle theme</span>
38+
</Button>
39+
)
40+
}

0 commit comments

Comments
 (0)