Theme Toggle
thilo custom
next-themes
Switches between Light, Dark, and System theme. Persists in localStorage.
@/components/ui/theme-toggleLive — click to switch
Opens a dropdown with Light / Dark / System options
The same toggle appears in the top-right corner of every page in the AppShell top bar.
Modes
| Mode | Description |
|---|---|
Light | Forces light theme regardless of OS setting. Adds no class to <html>. |
Dark | Forces dark theme. Adds class="dark" to <html>. |
System | Follows OS preference via prefers-color-scheme media query. |
How tokens flip between themes
Every semantic token has two values in globals.css — one for :root (light) and one for .dark. Switching theme swaps all values instantly.
| Token | Light value | Dark value | Tailwind class |
|---|---|---|---|
| --background | oklch(1 0 0) | oklch(0.145 0 0) | bg-background |
| --foreground | oklch(0.145 0 0) | oklch(0.985 0 0) | text-foreground |
| --card | oklch(1 0 0) | oklch(0.205 0 0) | bg-card |
| --muted | oklch(0.97 0 0) | oklch(0.269 0 0) | bg-muted |
| --border | oklch(0.922 0 0) | oklch(0.269 0 0) | border-border |
| --status-open | oklch(0.5 0.2 250) | oklch(0.7 0.2 250) | text-status-open-fg |
In context — placement in the top bar
thiloTicketsPartnersReports
DA
Page content area
How it works
| Layer | What it does |
|---|---|
| next-themes ThemeProvider | Wraps the app in layout.tsx. Reads/writes theme to localStorage. Sets class="dark" on <html>. |
| globals.css :root | Defines all semantic tokens as light values (oklch colors). |
| globals.css .dark | Overrides every token to dark values. No component code changes. |
| ThemeToggle component | Uses useTheme() hook from next-themes. Opens a DropdownMenu with 3 options. |
| All shadcn components | Already respond to .dark class automatically via CSS custom properties. |
This is why using semantic tokens (bg-card, text-foreground) everywhere is mandatory — any hardcoded primitive (bg-zinc-950) will not respond to theme switching.
On this page