Dark Mode Guide

CSS Custom Properties Approach

/* Step 1: Define light mode defaults */
:root {
  --bg: #ffffff;
  --surface: #f4f4f8;
  --text: #1a1a2e;
  --text-muted: #6b7280;
  --border: #e5e7eb;
}

/* Step 2: Override for system dark mode */
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f1117;
    --surface: #1c2035;
    --text: #e2e8f0;
    --text-muted: #8b92a5;
    --border: #2a2f4a;
  }
}

/* Step 3: Class-based toggle override */
[data-theme="dark"] {
  --bg: #0f1117;
  --surface: #1c2035;
}
[data-theme="light"] {
  --bg: #ffffff;
  --surface: #f4f4f8;
}

JavaScript Toggle with localStorage

// Prevent flash of wrong theme (add to <head>)
(function() {
  const saved = localStorage.getItem('theme');
  const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
  document.documentElement.dataset.theme = saved || (prefersDark ? 'dark' : 'light');
})();

// Toggle button logic
function toggleTheme() {
  const current = document.documentElement.dataset.theme;
  const next = current === 'dark' ? 'light' : 'dark';
  document.documentElement.dataset.theme = next;
  localStorage.setItem('theme', next);
}

Dark Mode Best Practices

RuleReason
Use #1c2035 not pure #000000 for backgroundsPure black causes too much contrast
Reduce saturation of colors in dark modeVibrant colors glow uncomfortably on dark
Invert images selectively (icons, diagrams)Photos look wrong when fully inverted
Add transition: color 0.2s ease for smooth switchAvoids jarring instant change
Test with users who have light sensitivityAccessibility is the primary use case