I like to dark mode all the things. But I also know lots of people who prefer light mode! To respect personal preferences and consider accessibility from the start, I add support for native light and dark mode as soon as I begin a new web project.
This solution uses no JavaScript, so we're not building a light and dark mode toggle. Instead, it detects the user's system settings with a CSS media query, and uses two custom CSS properties to determine a basic colour scheme. Here's how it's done.
Declare 2 CSS custom properties
CSS custom properties are also referred to as CSS variables or cascading variables. You can define CSS custom properties anywhere in your CSS files, and they follow the same cascading and specificity patterns as other CSS rules. For example, you can define CSS variables at the document root, and override them in more specific CSS classes. What's great is you can also inspect and debug declared CSS variables in browser dev tools, which appear below the stylesheet rules.
CSS custom properties are declared by words prefixed with two dashes (--), and accessed using the var()
function.
:root {
--my-color-variable: #000000;
}
.element {
/* This is calculated as #000000! */
color: var(--my-color-variable);
}
You can also pass a second parameter into the var()
function, which acts as a fallback value if the custom property doesn't exist when you try to use it.
.element {
color: var(--my-new-color, #ff0000);
}
For this light/dark mode solution, define two colour variables at the document root — one for the foreground colour, and one for the background colour. I tend to choose dark mode by default, so I set the background colour to black (--color-bg
) and the foreground colour to white (--color-fg
).
:root {
--color-bg: #000000;
--color-fg: #ffffff;
}
Use the prefers-color-scheme media query
Next, we're going to hook into system settings using the prefers-color-scheme CSS media query and flip the variable declarations for the background and foreground colours. The following code sets the --color-bg
to white and the --color-fg
to black when a light theme setting is detected.
@media (prefers-color-scheme: light) {
:root {
--color-bg: #ffffff;
--color-fg: #000000;
}
}
Add body styles
Finally, using the CSS custom properties, set the background-color
(for the page colour) and color
(for the text) on the HTML body
element, which all child elements will inherit if they're not overwritten.
body {
background-color: var(--color-bg);
color: var(--color-fg);
}
And that's it — support for native light and dark mode preferences in just 14 lines of CSS!
View the code on CodePen
Here's the full example on CodePen, which will display light or dark mode depending on your system preferences. Toggle your system settings to watch the CodePen switch themes!
How do you implement dark mode and light mode? Let me know on Twitter.
Top comments (29)
This is cool! Here's how you can do it with one line of CSS and achieve the same results:
Check out this URL: codepen.io/phelixdusengimana/pen/O...
Advantages of this approach include:
It automatically changes form controls' appearance to fit the user's chosen theme.
It changes the text color and scrollbar theme based on the user's preferred UI theme.
Read more: developer.mozilla.org/en-US/docs/W...
TIL!! Thanks for that! I guess if you wanted different shades of black and white for the foreground/background colours you’d still have to do a bit more work?
Sure. If you want different background other than the CSS built-in you'd use the first approach.
I try to change my mode to dark in Ubuntu, but nothing happened :)
Unfortunately, setting Ubuntu on dark mode won't set Chrome as well.
Thus, it's not as straightforward as Windows, and it will work a bit differently, but it's simple to set up.
I made this step-by-step guide to follow when changing chrome to dark mode in ubuntu: Link
This is the way. I've never been a fan of using Javascript to switch themes. I've never actually implemented it before but if and when I do I'll do it your way. It might not work on every system yet, I'm not sure? But it'll get there in time if it doesn't.
Here's a compatibility overview for the three major components to this implementation: caniuse.com/prefers-color-scheme,c...
The
prefers-color-scheme
is rather essential for pegging your css to the user's system theme, but the:root
pseudo class and even the custom properties / variables could be left out - although that'd mean nesting all your css within those media queries 😉I use a static blog generator with a light theme that I have overridden by hand. Now with this idea I can just wrap my overrides in the media query.
Sweet!
This is the way! 🥳
Look what's coming this year - Change the
accent-color
automatically when light/dark mode changes !Thanks, Salma! A very handy read you gave us here. And there are some interesting spin-offs from the other folks here too. Thanks to all of you for keeping us on our toes!
And I'm back! I accepted @waylonwalker 's challenge and the comment by @shammisharma about doing it with CSS only. And yes, it was done. A lady from Taipei, Mu-An Chiou is the original creator of the work.
The details of is is not evident in her brief portfolio, but it is explained in great German-style detail by a gent named Philipp. I went and checked Philipp's site which uses the CSS implementation and W3C's CSS validator passes it with an all-clear.
Here is the link for those interested: kleinfreund.de/css-only-dark-mode/
The CodePen can be found here for those that want to dash straight to the honeypot. : codepen.io/kleinfreund/pen/bJwqpB
Super cool snippet :)
Great snippet.
This is awesome, thank you. I just applied it to my #100daysofcode blog/journal. adriaanb.herokuapp.com
Great work!
Good job with the article 👍
I really like this topic and I also have written a thorough article about ”How to create dark mode using only CSS” dev.to/laurilllll/how-to-create-da...
Check it out 💪
What about using
developer.mozilla.org/en-US/docs/W...
Dark mode in 10 lines of code: codepen.io/inspiredlabs/pen/OJQpPg...
Interesting approach! Except I'm viewing it in dark mode which shows white text on a grey background — which I can't read 😅
Exactly! Using
var(--color-bg, inherit)
uses the browser's fallback value. Perhaps one day, browsers will simply have a algo to do this themselves, such as Luma: css-tricks.com/switch-font-color-f...