Designing for Dark Mode: Tailwind and CSS Variables
Dark mode isn't just inverting colors — it requires rethinking contrast, elevation, and hierarchy. Here's how to implement it properly.
CSS Variables Approach
/* globals.css */
:root {
--background: 255 255 255; /* #ffffff */
--foreground: 15 23 42; /* #0f172a */
--card: 248 250 252; /* #f8fafc */
--border: 226 232 240; /* #e2e8f0 */
--primary: 79 70 229; /* #4f46e5 indigo */
--muted: 148 163 184; /* #94a3b8 */
}
.dark {
--background: 2 6 23; /* #020617 */
--foreground: 248 250 252; /* #f8fafc */
--card: 15 23 42; /* #0f172a */
--border: 30 41 59; /* #1e293b */
--primary: 99 102 241; /* #6366f1 slightly lighter */
--muted: 71 85 105; /* #475569 */
}
Tailwind Config with CSS Variables
// tailwind.config.ts
export default {
darkMode: 'class', // Toggle with .dark class on <html>
theme: {
extend: {
colors: {
background: 'rgb(var(--background) / <alpha-value>)',
foreground: 'rgb(var(--foreground) / <alpha-value>)',
card: 'rgb(var(--card) / <alpha-value>)',
border: 'rgb(var(--border) / <alpha-value>)',
primary: 'rgb(var(--primary) / <alpha-value>)',
muted: 'rgb(var(--muted) / <alpha-value>)',
},
},
},
};
Dark Mode Toggle
'use client';
import { useTheme } from 'next-themes';
export function ThemeToggle() {
const { theme, setTheme } = useTheme();
return (
<button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
{theme === 'dark' ? '☀️' : '🌙'}
</button>
);
}
npm install next-themes
Semantic Color Usage
// Use semantic names, not literal colors
// WRONG: hardcoded color
<div className='bg-white text-gray-900 dark:bg-gray-950 dark:text-gray-50'>
// CORRECT: semantic variables
<div className='bg-background text-foreground'>
// Card component
<div className='bg-card border border-border rounded-lg p-6'>
// Muted text
<p className='text-muted'>Secondary information</p>
Elevation in Dark Mode
/* In dark mode, use lighter backgrounds for elevated surfaces */
/* NOT shadows — dark shadows on dark backgrounds don't work */
.dark .card { background: rgb(var(--card)); } /* Base */
.dark .dropdown { background: rgb(30 41 59); } /* Elevated +1 */
.dark .modal { background: rgb(51 65 85); } /* Elevated +2 */
System Preference Detection
// app/providers.tsx
import { ThemeProvider } from 'next-themes';
export function Providers({ children }) {
return (
<ThemeProvider
attribute='class'
defaultTheme='system' // Respects OS preference
enableSystem
>
{children}
</ThemeProvider>
);
}
Dark mode ships pre-configured in the AI SaaS Starter Kit — CSS variables, next-themes, semantic Tailwind colors, and toggle component included. $99 at whoffagents.com.
Top comments (0)