loading...
Cover image for Add dark mode to your website with just a few lines of code

Add dark mode to your website with just a few lines of code

albertomontalesi profile image AlbertoM Updated on ・6 min read

This article was originally posted on my blog. Head over to inspiredwebdev.com for more articles and tutorials. Check out my JavaScript course on Educative to learn everything from ES6 to ES2019.

 

Hi, I am Alberto Montalesi, a full-stack self-taught developer. I create practical JavaScript tutorials and courses on my website inspiredwebdev.com to inspire other developers to grow and build the career that they want.

Dark mode has become incredibly popular in the last year and all popular apps nowadays offer a toggle to turn it on.

In this short tutorial we are going to look at how to add support for dark mode in your website in different ways: first with just CSS and lastly with a toggle built with JavaScript.

 

Adding Dark Mode support with CSS

If you don't want to get too fancy with dark mode and just want your website to change color palette if the user is using a browser with dark mode enabled, CSS is going to be enough for you.

Inside of your CSS file, write this media query:

@media (prefers-color-scheme: dark) {
    /* all your styles for dark mode here */
}

With this media query, we can define a set of custom styles to apply when the user is browsing with dark mode enabled.

According to CanIUse.com the support is at around 78%, leaving out Internet Explorer and Edge (not Edge Chromium).

If you are not working on enterprise software where you really have to worry about users still using Internet Explorer, I think that this media query can work well.

The downside of this method is that your user won't have control over how they want to view your website so I would consider it only if you don't have time or don't want to implement a toggle.

 

Toggle Dark Mode with JavaScript

Let's now go one step further and implement a toggle for Dark Mode with JavaScript.

To be able to do that you will need to create two different stylesheets, one for each theme (light and dark). If you need, you can create a third stylesheet which will contain non-theme related styles.

The first step will be to define a default theme stylesheet in the head of our html like so:

<link id="theme" rel="stylesheet" type="text/css" href="light-theme.css" />

What we are going to do now is to create a button to swap that stylesheet with another one.

Go ahead and create a button like the following and put it wherever you like, preferably at the top of the page for quicker access by the user.

<button id="theme-toggle">Switch to dark mode</button>

Now that we have a simple button, let's add some JavaScript to it.
Open your JavaScript file and write the following lines of code:

// this one is jut to wait for the page to load
document.addEventListener('DOMContentLoaded', () => {

    const themeStylesheet = document.getElementById('theme');
    const themeToggle = document.getElementById('theme-toggle');
    themeToggle.addEventListener('click', () => {
        // if it's light -> go dark
        if(themeStylesheet.href.includes('light')){
            themeStylesheet.href = 'dark-theme.css';
            themeToggle.innerText = 'Switch to light mode';
        } else {
            // if it's dark -> go light
            themeStylesheet.href = 'light-theme.css';
            themeToggle.innerText = 'Switch to dark mode';

        }
    })
})

This code is simply adding an event listener to our button so that every time we click it it will look at the href of our stylesheet and toggle between dark and light. We are also changing the text of the button itself to reflect the change in the theme.

You can play around with the button itself and define some neat icons to better differentiate between dark and light themes.

If you try the above code you will see that when you click the button the stylesheet changes but there is one problem.

Can you guess what the problem may be?

If you are thinking that the next time a user comes back to the website they will have to click again the button to toggle again the theme, then you guessed right.

At the moment the user choice is not saved anywhere so once they leave the site and come back they will have to switch theme again.

Luckily there's a quick way that allows us to overcome this problem and that is LocalStorage.

 

Saving users' preferences in localStorage

As the name implies, preference set to it will be stored locally on your browser so if your user changes browser or device they will lose their choice of theme but most of the time they will probably come back to your site using the same device so this can be a quick and effective way to store their preference.

LocalStorage can store key value pairs and we can use it like this:

localStorage.setItem('theme', 'dark-theme.css');

Let's go ahead and add it to our previous code:

// this one is jut to wait for the page to load
document.addEventListener('DOMContentLoaded', () => {

    const themeStylesheet = document.getElementById('theme');
    const storedTheme = localStorage.getItem('theme');
    if(storedTheme){
        themeStylesheet.href = storedTheme;
    }
    const themeToggle = document.getElementById('theme-toggle');
    themeToggle.addEventListener('click', () => {
        // if it's light -> go dark
        if(themeStylesheet.href.includes('light')){
            themeStylesheet.href = 'dark-theme.css';
            themeToggle.innerText = 'Switch to light mode';
        } else {
            // if it's dark -> go light
            themeStylesheet.href = 'light-theme.css';
            themeToggle.innerText = 'Switch to dark mode';
        }
        // save the preference to localStorage
        localStorage.setItem('theme',themeStylesheet.href)  
    })
})

As you can see in the code above, on page load we do a check to see if there is a theme preference stored in localStorage by using localStorage.getItem('theme').

If we find something, we then apply it right away, restoring the user's preferences.

I've also updated the code that runs when we click a button, including this line localStorage.setItem('theme',themeStylesheet.href) to store the user selection.

Now we have a fully functional light-dark theme toggle that will remember the user selection, improving considerably the user experience.

What's great is that this took only 10 minutes to do!

Remember that you are not limited to a dark-light theme, you can have as many as you want and you can even get fancy with them.

A few last words about localStorage: its support is now more than 93% so you can confidently use it without worrying too much about legacy browsers since it's supported even on IE8-9-10!.

Another way to quickly store user preferences, other than localStorage, are sessionStorage which, as the name implies persists only for the current session until the browser session is active which does not suit our case scenario well.

One thing to know about both localStorage and sessionStorage is that they stick to the same-origin policy meaning that if you access your website over both Http and Https, your choice of the theme made over https won't be reflected over Http.

If you start wondering why your preference is not being saved, knowing this little caveat can help you avoid spending half an hour trying to guess what's the issue.

Update: Another way of achieving the same result, but with using only one styleheet would be to toggle a global class on the body.

Add this to your JavaScript

button.addEventListener('click', () => {
    document.body.classList.toggle('dark');
    localStorage.setItem('theme', document.body.classList.contains('dark') ? 'dark' : 'light');
});

if (localStorage.getItem('theme') === 'dark') {
    document.body.classList.add('dark');
}

And your css will look like the following:

/* Light mode */
body {
  background: #fff;
  color: #000;
}

/* Dark mode */
body.dark {
  background: #000;
  color: #fff;
}

Edit: Thanks to Thomas and Taufik for corrections and edits.


Thank you very much for reading. Follow me on DevTo or on my blog at inspiredwebdev or on twitter. Check out Educative.io for interactive programming courses.

Disclaimer: Links to Amazon and Educative are affiliate links, purchases you make will generate extra commissions for me. Thank you


book banner

Get my ebook on Amazon and Leanpub

Posted on by:

albertomontalesi profile

AlbertoM

@albertomontalesi

Author of the book/course 'The complete guide to modern JavaScript'. Creator of inspiredwebdev.com, co-creator of howtovietnamese.com

Discussion

markdown guide
 

Another idea: use single theme file and just toggle a global class name…

button.addEventListener('click', () => {
    document.body.classList.toggle('dark');
    localStorage.setItem('theme', document.body.classList.contains('dark') ? 'dark' : 'light');
});

if (localStorage.getItem('theme') === 'dark') {
    document.body.classList.add('dark');
}
/* Light mode */
body {
  background: #fff;
  color: #000;
}

/* Dark mode */
body.dark {
  background: #000;
  color: #fff;
}
 

Yes you are right, I might add it to the article for completion.

 

And even better, define some css variables to change everything in just a few lines of css (variables declaration).

 
 

Nice post !
But an error on example :

document.getElementByID

edit to

document.getElementById

And

<button id="theme-toggle">Switch to dark mode<button>

to

<button id="theme-toggle">Switch to dark mode</button>
 

Thanks, I've updated the post

 

According to CanIUse.com [prefers-color-scheme] support is at around 78%, leaving out Internet Explorer and Edge (not Edge Chromium).

Correct me if I'm wrong, but I don't believe browser support is enough for this feature to work properly - you also need a way to set your theme preferences in your OS. I think this is only available on Android Q (Android 10), and I am yet to find a way to set this as a preference in KDE. (No clue about OSX, Windows, and iOS, as I don't use them and had no need to research it so far).

I think it's a bit too early to adopt this, as most users will have older phones.

 

You can always fallback to light theme in case this feature is not supported.

 

Of course. Better yet, do use a toggle, and don't rely on something unreliable :)

Using both is the preferred option. Support in OS/Browsers for prefers-color-scheme will only increase. Which means if you implement now, you will support more users in the future, rather than doing it later.

Secondly, in initial hit of your site, you can set their preferred theme according to their preferred color schema (dark mode on first hit). If you only rely on a toggle, you force users to use the light mode regardless of their preferred setting.

Correct, combining both is the best idea.

Admittedly, I should have worded this better, as that is what I do. Check for a LocalStorage preference that I set when the user toggles dark mode, and check the schema if the key isn't set.

 

Awesome I will try it in my website

 

Cool, leave us a link when you implement it!

 

This is really cool but I was wondering if there would be performance issues when using it with React or frameworks like Angular, etc?

 

Works perfectly in React. I did this previously on a Gatsby project and it works perfectly with no noticeable performance implications. I'm not sure about Angular, but it should be the same.

 
 

So, why would you prefer this to CSS variables?

 

CSS Variables for one aren't supported in IE11 : caniuse.com/#feat=css-variables

So for enterprise development you need a ponyfill to support this.

Otherwise, you could certainly set up your theme.css to have a set of CSS Variables you reuse in the rest of your CSS files, then dynamically change between light-theme or dark-theme or other custom themes with JS

 

Can you elaborate more?

 

I was using scss and then nested styles on body.dark, so far. I will give this a shot but I think using a dark class on the body is more performant after the initial file has been read.

 

I think having two different files can save you having unnecessary load styles for a theme you don't use. I guess at the end of the day, both will work just fine.

 

The browser should also cache both CSS so the user may only notice a dip in performance on the first time they switch, whilst their cache lives.

 

I believe instead of maintaining a whole different css file, can't we do it using a single extra class to body (class="dark")? I believe this approach is simple, isn't it? Just a suggestion :)

 

Yes you can, it's a totally viable alternative.

 

The downside of this method is that your user won't have control over how they want to view your website so I would consider it only if you don't have time or don't want to implement a toggle.

If a user has system-wide dark mode, the chances they'll be okay with their web pages being dark too are high.

As a 100% dark-mode-everything person I am, pages without dark mode irritate me these day.

 

Yes, you are right. I just think that giving user's a choice is better.

 

Thanks. You may not need any if block using data attributes. I'm not sure. But this is really great article. Thank you.

 

Wow. Awesome. I'll try this in my next projects.

 

Don’t forget to share results with us!

 

How about using CSS variables? They can be changed via javascript also.

 

Thank you Alberto.
I'm going to add a dark mode to my side project tonight :)

 

Hi? I'm stuck at saving user preference; I want to make the effect on a site hosted on blogger? any quick tip?

 

Do you find prefers-color-scheme to actually be populated? When I first looked into this, that field was always null.

 

It works well on macos Catalina.

 
 

I wrote a similar article a few weeks back with React and Gatsby in mind

amitd.co/blog/implementing-dark-mode

 

Thanks for sharing! I will try this on my next project.

 

I want to implement dark mode on my community’s website. I add it to the backlog

 
 

Hey that is really a super, uber cool tip! I had been thinking of this but not really searched further. It is an easy bite for me. Thank you for sharing the insights!!