Dark Mode
All landing components support dark mode via Tailwind's dark: prefix.
How It Works
The theme is controlled by ThemeProvider which adds a dark class to the HTML element:
// src/contexts/ThemeContext.tsx
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
useEffect(() => {
document.documentElement.classList.toggle('dark', theme === 'dark');
}, [theme]);
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
Using Dark Mode Classes
Apply dark mode styles using Tailwind's dark: prefix:
// Background colors
className="bg-white dark:bg-zinc-950"
// Text colors
className="text-zinc-900 dark:text-white"
// Secondary text
className="text-zinc-600 dark:text-zinc-400"
// Borders
className="border-zinc-200 dark:border-zinc-800"
// Backgrounds with opacity
className="bg-zinc-100 dark:bg-zinc-900/50"
Color Palette
Use the zinc palette for consistent dark mode:
| Light Mode | Dark Mode | Usage |
|---|---|---|
bg-white |
bg-zinc-950 |
Page background |
bg-zinc-50 |
bg-zinc-900 |
Section background |
bg-zinc-100 |
bg-zinc-800 |
Card background |
text-zinc-900 |
text-white |
Primary text |
text-zinc-600 |
text-zinc-400 |
Secondary text |
border-zinc-200 |
border-zinc-800 |
Borders |
Configuration
Tailwind Config
Ensure dark mode is class-based in tailwind.config.js:
module.exports = {
darkMode: 'class',
// ...
};
Root Layout
Wrap your app with ThemeProvider:
// src/app/layout.tsx
import { ThemeProvider } from '@/contexts/ThemeContext';
export default function RootLayout({ children }) {
return (
<html suppressHydrationWarning>
<body>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
);
}
CSS Variables
Add dark mode color scheme:
/* globals.css */
.dark {
color-scheme: dark;
}
Theme Toggle
Use the useTheme hook for toggle buttons:
import { useTheme } from '@/contexts/ThemeContext';
import { SunIcon, MoonIcon } from '@/components/icons';
function ThemeToggle() {
const { theme, setTheme } = useTheme();
return (
<button
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
aria-label="Toggle theme"
>
{theme === 'dark' ? <SunIcon /> : <MoonIcon />}
</button>
);
}
Common Patterns
Gradient Backgrounds
// Light: blue gradient, Dark: zinc gradient
className="bg-gradient-to-b from-zinc-50 to-zinc-100 dark:from-zinc-900 dark:to-zinc-950"
Cards with Borders
className="bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-xl"
Hover States
className="hover:bg-zinc-100 dark:hover:bg-zinc-800"
Inverted Text on Dark Backgrounds
// For sections with dark backgrounds in both modes
className="bg-zinc-900 text-white dark:bg-zinc-800"
Troubleshooting
Dark mode not applying
- Check
ThemeProviderwraps your app - Verify
darkMode: 'class'in tailwind.config.js - Ensure
.darkclass is on<html>element
Flash of wrong theme
Add suppressHydrationWarning to html tag and initialize theme before render:
<html suppressHydrationWarning>
Images look wrong in dark mode
Use different images or apply filters:
className="dark:invert dark:brightness-0"
Or provide alternate images:
<Image src={isDark ? '/logo-dark.svg' : '/logo-light.svg'} />