# Dark Mode Toggle

Theme switching with next-themes and shadcn/ui.

## Setup with next-themes

### 1. Install

```bash
npm install next-themes
```

### 2. Add ThemeProvider

```tsx
// app/providers.tsx
'use client'

import { ThemeProvider as NextThemesProvider } from 'next-themes'

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <NextThemesProvider
      attribute="class"
      defaultTheme="system"
      enableSystem
      disableTransitionOnChange
    >
      {children}
    </NextThemesProvider>
  )
}
```

### 3. Wrap App

```tsx
// app/layout.tsx
import { Providers } from './providers'

export default function RootLayout({ children }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  )
}
```

## Toggle Component (Dropdown)

```tsx
'use client'

import * as React from 'react'
import { Moon, Sun } from 'lucide-react'
import { useTheme } from 'next-themes'

import { Button } from '@/components/ui/button'
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'

export function ModeToggle() {
  const { setTheme } = useTheme()

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="outline" size="icon">
          <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
          <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
          <span className="sr-only">Toggle theme</span>
        </Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent align="end">
        <DropdownMenuItem onClick={() => setTheme('light')}>
          Light
        </DropdownMenuItem>
        <DropdownMenuItem onClick={() => setTheme('dark')}>
          Dark
        </DropdownMenuItem>
        <DropdownMenuItem onClick={() => setTheme('system')}>
          System
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}
```

## Toggle Component (Simple Button)

```tsx
'use client'

import { Moon, Sun } from 'lucide-react'
import { useTheme } from 'next-themes'
import { Button } from '@/components/ui/button'

export function ThemeToggle() {
  const { theme, setTheme } = useTheme()

  return (
    <Button
      variant="ghost"
      size="icon"
      onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
    >
      <Sun className="h-5 w-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
      <Moon className="absolute h-5 w-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
      <span className="sr-only">Toggle theme</span>
    </Button>
  )
}
```

## Toggle Component (Switch)

```tsx
'use client'

import { useTheme } from 'next-themes'
import { Switch } from '@/components/ui/switch'
import { Label } from '@/components/ui/label'
import { Moon, Sun } from 'lucide-react'

export function ThemeSwitch() {
  const { theme, setTheme } = useTheme()
  const isDark = theme === 'dark'

  return (
    <div className="flex items-center gap-2">
      <Sun className="h-4 w-4" />
      <Switch
        id="theme-switch"
        checked={isDark}
        onCheckedChange={(checked) => setTheme(checked ? 'dark' : 'light')}
      />
      <Moon className="h-4 w-4" />
      <Label htmlFor="theme-switch" className="sr-only">
        Toggle dark mode
      </Label>
    </div>
  )
}
```

## Handling Hydration

```tsx
'use client'

import { useTheme } from 'next-themes'
import { useEffect, useState } from 'react'

export function ThemeAwareComponent() {
  const { theme, resolvedTheme } = useTheme()
  const [mounted, setMounted] = useState(false)

  // Avoid hydration mismatch
  useEffect(() => {
    setMounted(true)
  }, [])

  if (!mounted) {
    return <Skeleton /> // Or null
  }

  // Safe to use theme now
  return (
    <div>
      Current theme: {resolvedTheme}
    </div>
  )
}
```

## CSS Variables Setup

Ensure your CSS supports both themes:

```css
:root {
  --background: oklch(1 0 0);
  --foreground: oklch(0.145 0 0);
  /* ... other light mode variables */
}

.dark {
  --background: oklch(0.145 0 0);
  --foreground: oklch(0.985 0 0);
  /* ... other dark mode variables */
}
```

## Configuration Options

```tsx
<NextThemesProvider
  attribute="class"           // Use class strategy
  defaultTheme="system"       // Default to system preference
  enableSystem                // Enable system preference detection
  disableTransitionOnChange   // Prevent flash on theme change
  storageKey="theme"          // localStorage key
  themes={['light', 'dark', 'system']}  // Available themes
/>
```

## Tailwind v4 Dark Mode

In Tailwind CSS v4, dark mode uses a CSS-first approach. The `dark:` variant works automatically based on the `.dark` class or `prefers-color-scheme` media query.

```css
/* app.css - Tailwind v4 CSS-first approach */
@import "tailwindcss";

@theme {
  /* Define your theme tokens */
  --color-background: oklch(1 0 0);
  --color-foreground: oklch(0.145 0 0);
}

/* Dark mode via .dark class (used by next-themes) */
.dark {
  --color-background: oklch(0.145 0 0);
  --color-foreground: oklch(0.985 0 0);
}

/* Or automatic detection via media query */
@media (prefers-color-scheme: dark) {
  :root:not(.light) {
    --color-background: oklch(0.145 0 0);
    --color-foreground: oklch(0.985 0 0);
  }
}
```

No `tailwind.config.js` configuration needed - the `dark:` variant is enabled by default in v4 and responds to the `.dark` class on a parent element.
