CSS April 2026 · 9 min read

How to Add Dark Mode with CSS: Complete 2026 Guide

Dark mode has gone from novelty to expectation. Users set their OS to dark mode and expect every app and website to follow suit. Here's how to implement it correctly — with pure CSS, no JavaScript required for the automatic case.

The prefers-color-scheme Media Query

The simplest approach: use the prefers-color-scheme media query to detect the user's OS preference and apply dark styles automatically:

/* Default: light mode */
body {
  background: #ffffff;
  color: #1e293b;
}

/* Automatically applied when OS is in dark mode */
@media (prefers-color-scheme: dark) {
  body {
    background: #050508;
    color: #e2e8f0;
  }
}

The Right Way: CSS Custom Properties

Scattering dark mode overrides throughout your CSS leads to maintenance nightmares. The correct approach: define all colors as CSS custom properties and swap them in one place:

:root {
  /* Light mode (default) */
  --bg:           #ffffff;
  --bg-surface:   #f8fafc;
  --text:         #1e293b;
  --text-muted:   #64748b;
  --border:       #e2e8f0;
  --primary:      #7c5cfc;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg:           #050508;
    --bg-surface:   #0f0f14;
    --text:         #e2e8f0;
    --text-muted:   #94a3b8;
    --border:       rgba(255,255,255,0.08);
    --primary:      #9d7ffe; /* slightly lighter for dark bg contrast */
  }
}

/* All components reference tokens, never raw colors */
.card {
  background: var(--bg-surface);
  color: var(--text);
  border: 1px solid var(--border);
}

With this pattern, you write dark mode support once per token, not once per component. Every component automatically adapts to both modes.

Class-Based Toggle (Manual Dark Mode)

For a user-controlled toggle, use a .dark class on the <html> element instead of relying solely on the media query:

/* CSS: both media query and class-based */
:root { --bg: #ffffff; --text: #1e293b; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #050508;
    --text: #e2e8f0;
  }
}

[data-theme="dark"] {
  --bg: #050508;
  --text: #e2e8f0;
}

/* JavaScript: toggle */
const toggle = document.getElementById('theme-toggle');
toggle.addEventListener('click', () => {
  const current = document.documentElement.dataset.theme;
  const next = current === 'dark' ? 'light' : 'dark';
  document.documentElement.dataset.theme = next;
  localStorage.setItem('theme', next);
});

// On load: restore saved preference
const saved = localStorage.getItem('theme');
if (saved) document.documentElement.dataset.theme = saved;

Handling Images in Dark Mode

Images designed for light backgrounds can look harsh or wrong on dark backgrounds. A few strategies:

/* Reduce brightness of images in dark mode */
@media (prefers-color-scheme: dark) {
  img:not([src*=".svg"]) {
    filter: brightness(0.9) contrast(1.05);
  }
}

/* Use the picture element for mode-specific images */
/* (logos, illustrations that need different versions) */

SVG Dark Mode

SVG favicons and inline SVGs can respond to color scheme too. Use currentColor for CSS-inheritable colors, or embed a media query directly in the SVG file:

/* Inline SVG with currentColor */
.icon svg path {
  fill: currentColor; /* inherits from CSS color property */
}

/* SVG file with embedded media query */
/* In favicon.svg: */

  
  ...

Dark Mode Design Principles

Implementing dark mode technically is straightforward; designing it well takes care:

Common Dark Mode Mistakes

Related Guides

contrast

Contrast Checker

Verify dark mode color pairs

color_lens

Color Palette Generator

Generate dark mode palettes