Nowadays, more and more devices start to support dark mode. Therefore, we will have to create our website with dark mode support inevitably in the future. In this article, I will show you some tricks to enable dark mode on your website with just pure CSS. And also shows you a few tools and tips you can use when you're designing a website with dark mode support.
First of all, let introduce some CSS we will use on our examples.
1. CSS custom properties (CSS variables)
We can define a custom property and then use it through var()
/* define the property */
:root {
--main-color: black;
}
/* use the property */
p {
color: var(--main-color);
}
var()
support the second parameter as a fallback.
p {
color: var(--main-color, darkgray);
}
And we can use var()
inside the var()
.
p {
color: var(--main-color, var(--second-color));
}
2. Prefers-color-scheme
The prefers-color-scheme CSS media feature is used to detect if the user has requested the system to use a light or dark color theme.
@media (prefers-color-scheme: dark) {
... some style for dark mode
}
@media (prefers-color-scheme: light) {
... some style for light mode
}
Unfortunately, both attributes are not supported by IE. For CSS variables, I personally love this solution by using SASS mixin. And there are some others you can find on GitHub.
Examples
Now we know how to use those CSS attributes. Let's jump into our examples.
1. Load the system setting
The idea is pretty straightforward. Just set all the colors as custom properties. Then do the media query to switch the setting.
/* set light mode color as default value */
:root {
--bg-color: #ffffff75;
...
}
/* switch to dark mode color */
@media (prefers-color-scheme: dark) {
--bg-color: #121212;
...
}
/* apply CSS varaible through var() */
* {
background: var(--bg-color);
...
}
https://codepen.io/oahehc/pen/wvBqjMR
2. Switch with toggle-button
Because some devices aren't supported dark mode yet. So we still have to provide the toggle button for our users to switch from light and dark mode.
Here we use the checkbox to allow us to access the checked status through CSS.
/* light mode colors */
:root {
--bg-color: #ffffff75;
...
}
/* apply dark mode colors when button is checked */
input:checked, input:checked + * {
--bg-color: #121212;
...
}
/* apply CSS varaible through var() */
* {
background: var(--bg-color);
...
}
https://codepen.io/oahehc/pen/OJPjZXB
3. Combine both of them
For better user experience, we can combine both examples above. Load the system setting as the default. Then displays the toggle-button for the user to customize.
<!-- we will need two buttons -->
<input class="dark" type="checkbox" />
<input class="light" type="checkbox" />
<article>
...
</article>
/* deafult light mode color */
:root {
--bg-color: #ffffff75;
...
}
/* hide light button if system setting is light mode */
@media (prefers-color-scheme: light) {
.light {
display: none;
}
}
/* hide dark button if system setting is dark mode and apply dark mode colors */
@media (prefers-color-scheme: dark) {
.dark {
display: none;
}
--bg-color: #121212;
...
}
/* swith to dark mode when dark button checked */
.dark:checked,
.dark:checked + * + * {
--bg-color: #121212;
...
}
/* swith to light mode when light button checked */
.light:checked,
.light:checked + * {
--bg-color: #ffffff75;
...
}
/* apply css variable */
* {
background: var(--bg-color);
...
}
https://codepen.io/oahehc/pen/GRgvdxw
Design Guideline & Tools
There are some properties that come from the material design that is worth to take a look.
- Contrast: Dark surfaces and 100% white body text have a contrast level of at least 15.8:1
- CDepth: At higher levels of elevation, components express depth by displaying lighter surface colors
- CDesaturation: Primary colors are desaturated so they pass the Web Content Accessibility Guidelines' (WCAG) AA standard of at least 4.5:1 at all elevation levels
- CLimited color: Large surfaces use a dark surface color, with limited color accents (light, desaturated and bright, saturated colors)
Use Chrome dev-tool can help us check that.
And here is an online tool can help us generate the color with different tone- Color Shades Generator.
--
Conclusion
That's everything I want to share with you. Thanks for the reading and I hope this article can help you to enable the dark mode on your website. Feel free to leave any comments or suggestions.
Reference
- https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties
- https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme
- https://stackoverflow.com/questions/44271920/css-variables-with-fallback-for-older-browsers
- https://material.io/design/color/dark-theme.html
- https://javisperez.github.io/tailwindcolorshades/#/
Top comments (6)
Nice one! I liked how you used
input:checked
to avoid using is!One thing though, since CSS variables follow regular CSS cascading, you don't need to use complex queries like
.light:checked + * > p
, you can simply use.light:checked
Thanks for the hint. I simplify the example base on your suggestion.
Good article, and nice solution, but you know it's not something that can actually work well on a website.
Users expect the change to remain across all the sub pages of the website. And they also expect it to remain the same as they have set last time had visited the site, especially if they did it logged in.
So in this case you need to use session and database where you save the theme which the user chose.
And of course none of these can be done using CSS only.
Thanks for the reminder., you are right, cross-page and save the last modification should be including and it's not achievable without js (at least base on what I know).
To achieve that, I will sync the input checked status from localStorage (through js of course).
The reason I want to prevent using
getPropertyValue
andsetProperty
to handle CSS variable through Javascript directly is that I think to make CSS and Javascript have less dependency as possible will make the code more maintainable.Typo first block --main-color: balck;
thank you~