DEV Community

Atlas Whoff
Atlas Whoff

Posted on

Designing for Dark Mode: Tailwind and CSS Variables

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 */
}
Enter fullscreen mode Exit fullscreen mode

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>)',
      },
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode
npm install next-themes
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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 */
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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)