DEV Community

Andrew
Andrew

Posted on • Edited on

How to Enable Dark Mode on Your Website with Pure CSS?

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);
}
Enter fullscreen mode Exit fullscreen mode

var() support the second parameter as a fallback.

p {
  color: var(--main-color, darkgray);
}
Enter fullscreen mode Exit fullscreen mode

And we can use var() inside the var().

p {
  color: var(--main-color, var(--second-color));
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen 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);
  ...
}
Enter fullscreen mode Exit fullscreen mode

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);
  ...
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode
/* 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);
  ...
}
Enter fullscreen mode Exit fullscreen mode

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.
dev-tool

And here is an online tool can help us generate the color with different tone- Color Shades Generator.
color-shade

--

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

Top comments (6)

Collapse
 
dorshinar profile image
Dor Shinar

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

Collapse
 
oahehc profile image
Andrew

Thanks for the hint. I simplify the example base on your suggestion.

Collapse
 
weeb profile image
Patrik Kiss • Edited

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.

Collapse
 
oahehc profile image
Andrew

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 and setProperty 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.

Collapse
 
jasonspd profile image
Jason Luu

Typo first block --main-color: balck;

Collapse
 
oahehc profile image
Andrew

thank you~