On this page

Theme System (Dark/Light Mode)

Build a theme system with CSS variables and next-themes for dark/light mode

The theme system uses CSS variables for flexibility and next-themes for seamless dark/light mode switching. CSS variables allow instant theme changes without re-rendering components — just swap the variable values. next-themes handles system preference detection, persistence in localStorage, and prevents the infamous flash of wrong theme on page load. I define color tokens as HSL values which makes it easy to create consistent color palettes and adjust lightness for different states (hover, active, etc.).

I use next-themes + CSS variables for its simplicity and Next.js integration. Alternatively, you could implement theme switching with React Context, Zustand, or CSS-in-JS solutions like styled-components or Emotion. The CSS variables pattern works with any approach — the key is defining semantic color tokens that adapt to each theme.

How it works

lib/
└── providers/
    └── theme-provider.tsx    # Theme provider wrapper

components/
└── common/
    └── ThemeToggle.tsx       # Theme toggle button

styles/
└── globals.css               # CSS variables for themes

tailwind.config.ts            # Tailwind theme configuration
app/
└── layout.tsx                # Root layout with provider

CSS Variables

Use HSL color format for easy theme customization and consistent colors.

Class-based

Using Tailwind's class strategy with next-themes for seamless switching.

System Preference

Auto detect and apply user's OS theme preference.

No Flash

Prevents flash of unstyled content with suppressHydrationWarning.

Key Features

  • CSS variables for flexible theming
  • System preference detection
  • Persistent theme selection
  • No flash on page load
  • Easy to add new themes

Live Example

Theme Switcher Demo

Preview

Card Title

This is how your content looks in light mode.

Color Palette

Primary
Background
Foreground
Card