DEV Community

Cover image for Add Dark Mode to your Websites with CSS
Dom (dcode)
Dom (dcode)

Posted on

Add Dark Mode to your Websites with CSS

It's super easy to include a dark theme for your existing websites using CSS. In this tutorial, we're going to do this by taking advantage of CSS Variables.

We're going to have 3 different options for the theme - Auto, Light and Dark. The Light and Dark themes are pretty self explanatory, but the Auto theme is going to use the operating system theme setting to decide whether the site is going to be Light or Dark.

While I won't be showing you how to do this particular layout or include the content, here is an example of what we could create:

Example of usage

Adding the HTML

Let's start with the HTML, you can think of the value attribute as being the identifier for each theme:

<select id="theme">
    <option value="auto">Auto</option>
    <option value="light">Light</option>
    <option value="dark">Dark</option>
</select>
Enter fullscreen mode Exit fullscreen mode

Adding the CSS

Let's now add a bit of CSS to the body element, here is where you specify your colors for the Light Theme using CSS Variables:

body {
    --background-color: #ffffff;
    --text-color: #000000;
}
Enter fullscreen mode Exit fullscreen mode

Next, you'll want to make use of your CSS Variables throughout your style sheet - this is key to how our solution is going to work. For example, you might do:

.main-content {
    background: var(--background-color);
    color: var(--text-color);
}

button {
    color: var(--text-color);
}
Enter fullscreen mode Exit fullscreen mode

We're going to be implementing a dark theme by simply replacing the values of the above declared variables in instances where we're going to be using a dark theme. Let's add this CSS:

:root {
    --dark-background-color: #111111;
    --dark-text-color: #eeeeee;
}

body.theme-dark {
    --background-color: var(--dark-background-color);
    --text-color: var(dark-text-color);
}
Enter fullscreen mode Exit fullscreen mode

Now, if you add the theme-dark class to your <body> element, you should see the dark theme working. Shortly we'll be using JavaScript to toggle this value with the <select>, but let's implement our Auto option now:

@media (prefers-color-scheme: dark) {
    body.theme-auto {
        --background-color: var(--dark-background-color);
        --text-color: var(--dark-text-color);
    }
}
Enter fullscreen mode Exit fullscreen mode

The above CSS uses Media Queries which is checking if the operating system prefers a Dark Theme, and if so, we want to apply the nested ruleset for body.theme-auto.

We're basically saying "Does the operating system prefer dark mode, and does the <body> have a class of theme-auto? If so, let's use Dark Mode."

Try it out by changing your OS theme color, or even better, view the website on your phone with Dark Mode enabled.

Adding the JavaScript

Now that our CSS is working, we can move onto getting our theme selector drop-down to work. Let's add the following JavaScript:

function applyTheme(theme) {
    document.body.classList.remove("theme-auto", "theme-light", "theme-dark");
    document.body.classList.add(`theme-${theme}`);
}

document.addEventListener("DOMContentLoaded", () => {
   document.querySelector("#theme").addEventListener("change", function() {
        applyTheme(this.value);
   });
});
Enter fullscreen mode Exit fullscreen mode

Here, we are waiting for the DOM to be ready for us to start using it, and once it's ready, we are listening for when the user chooses an option in the theme selector drop-down. Once they choose an option, we remove all existing theme classes from the <body> (if any) and then simply add the selected theme with this.value.

A step further - remembering the theme

We could take this a step further and have the ability for the browser to remember the theme that was chosen upon a page refresh. To do this, we can use Local Storage

Let's add the following JavaScript, so we end up with this:

document.addEventListener("DOMContentLoaded", () => {
    const savedTheme = localStorage.getItem("theme") || "auto";

    applyTheme(savedTheme);

    for (const optionElement of document.querySelectorAll("#theme option")) {
        optionElement.selected = savedTheme === optionElement.value;
    }

    document.querySelector("#theme").addEventListener("change", function () {
        localStorage.setItem("theme", this.value);
        applyTheme(this.value);
    });
});
Enter fullscreen mode Exit fullscreen mode

Now, upon choosing a theme, we save the theme to Local Storage by using localStorage.setItem("theme", this.value). Following this up, on page load, we grab the previously saved theme into the savedTheme constant, with a default of auto. Once we have this, we simply apply the saved theme.

Adding to this, we are then looping through each one of our <option> elements and checking to see if the value is that of our saved theme, and if so, choose that option as "selected".

To test if it works, refresh the page, choose a theme, refresh again, and your theme should stick!

Video Tutorial

If you instead prefer this in the form of a video tutorial, check it out here on my YouTube channel, dcode!

Hope you guys enjoyed this one! This was my first DEV post so if you have any recommendations for improvement, please let me know.

Cheers!😁

Oldest comments (21)

Collapse
 
coderarchive profile image
Lu-Vuong Le πŸš€

Nice one! πŸ‘

Collapse
 
dcodeyt profile image
Dom (dcode)

Thanks mate!

Collapse
 
supermario_ai profile image
SuperMario

β€οΈπŸ’―

Collapse
 
rupekeshan profile image
rupekeshan

Nice but this method is not supported by IE

Collapse
 
okikio profile image
Okiki Ojo

But no one will be using IE anymore since Edge Chromium has all of IE's legacy features baked in.

Collapse
 
icidel profile image
del65 • Edited

For IE you could simply have a "theme-dark.css" and "theme-light.css" (and also "theme-print.css" for... Printing) then use server or JavaScript code to include the suitable CSS file on the fly.

Collapse
 
jatinpalande profile image
Jatinpalande

Great one I loved itt πŸ˜‡πŸ˜‡πŸ₯°

Collapse
 
dcodeyt profile image
Dom (dcode)

You're welcome 😊

Collapse
 
tarash1 profile image
Taras

Good solution!

Collapse
 
dcodeyt profile image
Dom (dcode)

Thanks!

Collapse
 
andreabaccolini profile image
Andrea

Very beautifull!
But if I choose the light theme and close the site, the next time I open the site in light mode but the selector is on "auto".

Collapse
 
dcodeyt profile image
Dom (dcode)

That's strange, does it work for other modes?

Collapse
 
andreabaccolini profile image
Andrea

I explain better perhaps you don't understand:
1 I choose the light mode
2 I close the site with the light mode on
3 I reopen the site
3 the select shows me the voice auto instead of light

Thread Thread
 
dcodeyt profile image
Dom (dcode)

I just copied the code directly out of the article and it appears to work fine. Do you have any errors coming up? Or a typo perhaps?

I've also made the code available here:
codepen.io/dcode-software/pen/ZEQMEjG

Thread Thread
 
andreabaccolini profile image
Andrea • Edited

No i havent any error

EDIT now is correct i had a problem with w3 Total Cache

Collapse
 
m4r4v profile image
m4r4v

Simple and clean. Well done!!

Collapse
 
dcodeyt profile image
Dom (dcode)

Thanks 😊

Collapse
 
sephiano1 profile image
Ihaza Omosefe

We can also approach it with SASS

Collapse
 
icidel profile image
del65

I think loading jQuery is overkill to only keep a single value in a cookie or local storage.

I prefer to stay as close to W3C standards as possible : most of the time a commented code snippet can do the job as well, and greatly reduce potential security flaws versus any library.

Collapse
 
andreabaccolini profile image
Andrea • Edited

What if I wanted to do it with icons (or buttons) instead of with a select?

Collapse
 
master___yt profile image
mASTEr.yt

There is an error:
--text-color: var(dark-text-color);
should be
--text-color: var(--dark-text-color);