Hey Devs! Let's Talk Dark Mode.
If you're building a website today, adding a Dark Mode isn't just a cool feature—it's practically mandatory. It's an accessibility win, and frankly, people just prefer it when they're coding at 2 AM.
But we need to stop just slapping an filter: invert(100%) on the whole page. That looks awful. We want a smooth, custom, and professional switch.
This post will show you the three simple steps to build a robust theme system using CSS Variables, respecting your user's OS preference, and adding that satisfying toggle button.
Step 1: CSS Variables are Your Best Friends 🎨
The secret sauce here is CSS Custom Properties (aka variables). Instead of defining colors like "blue" or "black," we define them by their function. That way, we only have to change the variable values once to switch the whole theme!
Check out this structure for your main CSS:
CSS
/* LIGHT THEME: The default. Defined in the main :root /
:root {
--color-bg-primary: #ffffff; / Main background is white /
--color-text-primary: #121212; / Text is a dark gray /
--color-accent: #007bff; / Standard blue for links /
--shadow-default: rgba(0, 0, 0, 0.1); / Light, subtle shadow */
}
/* DARK THEME: Overwrite the variables inside a data-theme attribute /
[data-theme="dark"] {
--color-bg-primary: #1e1e1e; / Main background is deep gray /
--color-text-primary: #f9f9f9; / Text is near-white /
--color-accent: #8ab4f8; / Lighter blue that pops on dark /
--shadow-default: rgba(255, 255, 255, 0.05); / Very subtle white shadow */
}
/* Usage - this never changes, only the variable value does! /
body {
background-color: var(--color-bg-primary);
color: var(--color-text-primary);
transition: background-color 0.3s, color 0.3s; / Don't forget the smooth transition! */
}
Pro Tip: Avoid pure black (#000) and pure white (#fff). The contrast is painful in Dark Mode. Use softer dark grays and near-white colors for a much gentler look.
Step 2: Honor the User's System Preference 🖥️
If someone's OS is already set to Dark Mode, we should respect that immediately on page load to prevent a nasty FOUC (Flash of Unstyled Content). We need to do this with a tiny bit of JavaScript before the rest of the page loads.
Place this script right after your opening
tag:HTML
const storageKey = 'theme-preference';
function getTheme() {
// 1. Check for manual choice (saved in storage)
const storedTheme = localStorage.getItem(storageKey);
if (storedTheme) return storedTheme;
// 2. Fall back to the OS setting
return window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
}
// Set the initial theme right away on the <html> element
document.documentElement.setAttribute('data-theme', getTheme());
This script ensures the user sees the right theme instantly, preventing that annoying white flash if they prefer dark.
Step 3: Add the Toggle Button 🌙
Users love having control. Let's add a simple button to override the system setting and, critically, save their preference in localStorage.
HTML for the Toggle:
HTML
🌓
JavaScript for the Logic:
JavaScript
document.addEventListener('DOMContentLoaded', () => {
const toggleButton = document.getElementById('theme-toggle');
const storageKey = 'theme-preference'; // Re-use the key from the script above!
const setTheme = (theme) => {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem(storageKey, theme);
};
toggleButton.addEventListener('click', () => {
/ Get the current theme from the HTML attribute
const currentTheme = document.documentElement.getAttribute('data-theme');
// Determine the new theme
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
setTheme(newTheme);
});
});
Top comments (0)