In short
To add dark mode with CSS, store your colors in variables, switch them with prefers-color-scheme, and optionally let users pick a theme with a toggle. How do you detect dark mode with prefers-color-scheme? Most phones and computers have a light or dark setting.
To add dark mode with CSS, store your colors in variables, switch them with prefers-color-scheme, and optionally let users pick a theme with a toggle.
How do you detect dark mode with prefers-color-scheme?
Most phones and computers have a light or dark setting. The prefers-color-scheme media query lets your CSS read that setting. It works like any other media query, but it checks the user's theme instead of the screen width.
The best way to build a theme is with CSS variables. You define your colors once, then only swap the variable values for dark mode. Your components never need to change.
:root {
--bg: #ffffff;
--text: #1f2937;
--surface: #f3f4f6;
--accent: #4f46e5;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #111827;
--text: #e5e7eb;
--surface: #1f2937;
--accent: #818cf8;
}
}
body {
background: var(--bg);
color: var(--text);
font-family: system-ui, sans-serif;
}
.card {
background: var(--surface);
border-left: 4px solid var(--accent);
padding: 1rem;
border-radius: 8px;
}
Change your computer's theme to dark, and the page switches automatically. The body and .card rules never mention dark mode. They only use variables.
Tip: In dark mode, avoid pure black
#000backgrounds and pure white text. A very dark gray with light gray text is softer on the eyes. Also make bright accent colors a bit lighter, so they keep good contrast.
What does the color-scheme property do?
Your styles are not the only thing on the page. The browser also draws form fields, scrollbars and the default page background. The color-scheme property tells the browser which themes your page supports, so it can draw those parts in the right colors.
:root {
color-scheme: light dark;
}
This one line says, "My page supports both themes." In dark mode, text inputs, checkboxes and scrollbars turn dark too. Without it, you might get a bright white input box in the middle of your dark page.
Using the light-dark() function
The light-dark() function is a shorter way to write theme colors. You give it two colors: the first for light mode, the second for dark mode. It works in all modern browsers, but it only works when color-scheme is set.
:root {
color-scheme: light dark;
--bg: light-dark(#ffffff, #111827);
--text: light-dark(#1f2937, #e5e7eb);
--surface: light-dark(#f3f4f6, #1f2937);
--accent: light-dark(#4f46e5, #818cf8);
}
This does the same job as the media query example, but each color sits on one line with its dark partner. It is easier to read and harder to forget a color.
Common mistake: Using
light-dark()withoutcolor-scheme: light dark. The browser then always picks the light color, and dark mode seems "broken".
How to make a dark mode toggle button
Some users want a different theme on your site than on their device. A toggle button lets them choose. The trick is a data-theme attribute on the <html> element, with one small line of JavaScript to change it.
Because light-dark() follows color-scheme, you only need to change color-scheme for each theme:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Dark mode demo</title>
<style>
:root {
color-scheme: light dark;
--bg: light-dark(#ffffff, #111827);
--text: light-dark(#1f2937, #e5e7eb);
}
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }
body {
background: var(--bg);
color: var(--text);
font-family: system-ui, sans-serif;
padding: 2rem;
}
</style>
</head>
<body>
<button onclick="document.documentElement.dataset.theme = document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark'">Toggle theme</button>
<h1>My blog</h1>
<p>This page follows your device theme until you press the button.</p>
</body>
</html>
At first, the page follows the device theme. When you click the button, the JavaScript line sets data-theme="dark" or data-theme="light" on <html>. That forces color-scheme to one value, and every light-dark() color follows it.
Note: This simple version forgets the choice when the page reloads. Saving it needs a little more JavaScript, which you will learn in the JavaScript course.
Practice exercise
Take any page you have built and add dark mode. Move all your colors into variables on :root, write them with light-dark(), and add color-scheme: light dark. Test it by switching your device theme.
Hint: In Chrome DevTools, open the Rendering panel and use "Emulate CSS media feature prefers-color-scheme" to switch themes without changing your system settings.
Key takeaways
@media (prefers-color-scheme: dark)reads the user's device theme.- Store colors in variables and only swap the values for dark mode.
color-scheme: light darkmakes form fields and scrollbars match the theme.light-dark(lightColor, darkColor)puts both colors on one line, but needscolor-scheme.- A
data-themeattribute plus one JS line can force a theme from a toggle.