How to Add a Darkmode to Your Website

Written by fcol | Published 2022/05/31
Tech Story Tags: web-development | web-design | dark-mode | dark-mode-vs-light-mode-debate | get-dark-mode-on-my-website | dark-mode-website | how-to-add-dark-mode | website-design

TLDRAdd an element which can be clicked. Chuck the JS into a file or script tag (fill js at the bottom of the Javascript section). Make a bunch of CSS declarations with body.dark at the start of them...via the TL;DR App

Before we start I would like to preface this by saying this is an article about the programmatic side of dark mode. For design-cetric information, see this super useful and detailed article by @abhirajbhttps://hackernoon.com/how-to-implement-dark-mode-5-essential-tips-to-remember

Technologies Used

  • HTML
  • CSS
  • Vanilla JS (localStorage)

Righty-ho, let’s start.

So you wanna add darkmode to your website because that’s what all the cool kids are doing. I was in the same position, then I did a bit of googling and now I’m the master so sit down, open your editor and listen to my words.

We want four things.

  1. To know what the clients system preference for dark mode.

  2. To know what the clients website preference for dark mode.

  3. To listen to whether the clients system preference has changed.

  4. To listen to whether the website preference has changed.

The HTML…

Here is my wicked cool website. I created an index.html in the root of an empty directory.

<!DOCTYPE html>
<html>
    <head lang="en">
        <meta charset="utf-8">
        <title>DARKMODE - Rules</title>
        <script src="darkmode.js"></script>
    </head>
    <body>
        <h1>My awesome website</h1>
        <p>This website is a website.</p>

        <!-- This is the button which will toggle dark mode -->
        <button id="darkmode-button">Darkmode</button>
    </body>
</html>

As you can see, there is a button element. We will use this to toggle dark mode on or off. There is also a link to two external resources, a javascript file to hold the logic for darkmode and css where the style rules for darkmode live.

This is what it looks like now, without dark mode enabled

The Javascript

Now is time to build the logic for determining when darkmode is applied.

Let’s write a little logic table to make sure our code follows a sensible set of rules. I want the website preference to take precedence over the system preference when toggled and the system preference to take precedence over the website preference when that is toggled. But you can choose your own logic, this just makes sense in my warped brain. It also offers a nice recipe to write code from.

Logic ID

Users Operating System Default

Website preference

Darkmode Outcome

1

Light

Light or None

Light

2

Light

Dark

Dark

3

Dark

Dark or None

Dark

4

Dark

Light

Light

5

Light

Light (Toggled)

Light

6

Light

Dark (Toggled)

Dark

7

Dark

Dark (Toggled)

Dark

8

Dark

Light (Toggled)

Light

9

Light (Toggled)

Light

Light

10

Light (Toggled)

Dark

Light

11

Dark (Toggled)

Dark

Dark

12

Dark (Toggled)

Light

Dark

Now, open up darkmode.js and start typing…

  1. Making sure my uber complex site has loaded first…
// This checks to make sure the DOM has loaded properly before we start messing with it
document.addEventListener('DOMContentLoaded', function(event) {

  // DM logic in here...

}

The rest of the code will be inside this event listener.

  1. Selecting the toggle button and body elements…

// Select the body element
let body = document.querySelector('body')

// Select the dark mode button
let darkMode = document.querySelector('#darkmode-button');

It will allow us to test if the dark mode button has been clicked and attach a class to the body element.

  1. Setting up helper functions…

These will make the page dark or light.

We will do two things in our functions. The first is add a “dark“ class to our body element. This will trigger the css classes to be applied to the page. Secondly, add our chosen preference to local storage in the browser to we can persist the dark mode preference across all pages on the website and also remember the preference for when the user returns.

// Helper Functions
const makeDark = () => {
        body.classList.add("dark");
        localStorage.setItem('darkMode', 'dark');
}

const makeLight = () => {
        body.classList.remove("dark");
        localStorage.setItem('darkMode', 'light');
}

  1. Melt the butter and add the flour…

Check on startup for system preferences & check for local browser preferences. This is dealing with Logic ID’s 1 through 4 on my logic table.

After the helper functions add the following code.

// 1-4
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light').matches && localStorage.darkMode !== 'dark') {
    // System is light, website is light or no preference = Make light
    makeLight();
} 
else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light').matches && localStorage.darkMode === 'dark') {
    // System is light, website is dark = Make dark
    makeDark();
}
else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark').matches && localStorage.darkMode !== 'light') {
    // System is dark, website is dark = Make dark
    makeDark();
}
else {
    // System must be dark, website must be light or no preference
    makeLight();
}

  1. Dealing with the user clicking the button…

Listen for user clicks and dealing with logic IDs 5-8, which basically says if the user changes the darkmode, do exactly what they want. Therefore we can disregard the system preferences.

// Add an event listener which checks for the button being pressed, do exactly what the button says
darkMode.addEventListener('click', function() {
    if (localStorage.darkMode === 'light') {
        makeDark();
    } else {
        makeLight();
    }
});

  1. When the system switches preference

This is where you could argue that user preference of the website should take precedence, but I think; if the user has not set a preference on the website and they have their system change dark mode preference based on the time of day, then the website should follow this.

Also if they decide the system needs to be a specific preference and change to it manually, then the website should respect that decision and the user can change the website preferences if they do not want it to match system preferences. You could also add another button or a toggle which deals with this idea as another user preference.

Rant over…

Dealing with system preference changes

// 9-12 Add an event listener which checks for system preference changes, do exactly what it says
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
    if (event.matches) {
            makeDark();
    } else {
            makeLight();
    }
});

Javascript file is finished!

The final JS file…

// This checks to make sure the DOM had loaded properly before we start messing with it
document.addEventListener('DOMContentLoaded', function(event) {

    // Select the body element
    let body = document.querySelector('body')

    // Select the dark mode button
    let darkMode = document.querySelector('#darkmode-button');


    // Helper Functions
    const makeDark = () => {
        body.classList.add("dark");
        localStorage.setItem('darkMode', 'dark');
    }

    const makeLight = () => {
        body.classList.remove("dark");
        localStorage.setItem('darkMode', 'light');
    }


    // 1-4
    if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light').matches && localStorage.darkMode !== 'dark') {
        // System is light, website is light or no preference = Make light
        makeLight();
    } 
    else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light').matches && localStorage.darkMode === 'dark') {
        // System is light, website is dark = Make dark
        makeDark();
    }
    else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark').matches && localStorage.darkMode !== 'light') {
        // System is dark, website is dark = Make dark
        makeDark();
    }
    else {
        // System must be dark, website must be light or no preference
        makeLight();
    }


    // 5-8 Add an event listener which checks for the button being pressed, do exactly what the button says
    darkMode.addEventListener('click', function() {
        if (localStorage.darkMode === 'light') {
            makeDark();
        } else {
            makeLight();
        }
    });

    
    // 9-12 Add an event listener which checks for system preference changes, do exactly what it says
    window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
        if (event.matches) {
                makeDark();
        } else {
                makeLight();
        }
    });
})

Finally, the CSS

So far, all we have seen is the body tag toggle with a .dark class and the local storage change.

Here, we are going into darkmode.css, and adding darkmode specific styling. All I need for my site is two declarations and two selectors. Yours will likely be more complex. But remember all you need to do to change something in dark mode is write a declaration with body.dark preceding the selector. It is likely lots of elements in your page will have a similar style, so you can chain the selectors as I have done below…

/* Darkmode Styling */

body.dark,
body.dark button  {
    background-color: black;
    color: white;
}

THANKS FOR READING, Enjoy your websites cool new dark mode!


Written by fcol | Simple stuff.
Published by HackerNoon on 2022/05/31