DEV Community

Cover image for Create a Dark/Light Mode Toggle using JavaScript & LocalStorage 💫
Zoe
Zoe

Posted on

Create a Dark/Light Mode Toggle using JavaScript & LocalStorage 💫

Hi There! 👋

In this Article, I want to show you how you can create a simple light / dark mode toggle using JavaScript and LocalStorage.

Here is one example made using Toggle; Try Clicking the top right icon to see the themes change!


(Just a friendly reminder that you can also check the pure HTML / CSS code by clicking "view compiled" on CodePen!)

Let's get started!

The HTML

<!-- dark theme toggle -->
<label id="theme-switch" class="theme-switch" for="checkbox_theme">
  <input type="checkbox" id="checkbox-theme">
  <label for="checkbox-theme" class="fas"> </label>
</label>
<!-- Some general elements -->
<div class="wrapper">
  <h1>Dark Theme Toggle</h1>
  <p>Click the top right icon to switch modes</p>
</div>
Enter fullscreen mode Exit fullscreen mode

We will be using the checkbox property and Font Awesome Icons for the toggle switch. Basically, we will set a different icon if the checkbox is checked using CSS!

CSS

Let's first set up the document color scheme using CSS variables;
One for light mode (default), and another for dark mode.
Feel free to adjust the colors to your own preference!

/* light mode */
:root {
  --background: #f6f6f6;
  --card: #ffffff;
  --border: #cccccc;
  --table-header: #e6e6e6;
  --primary-text: #000000;
  --secondary-text: #212121;
}

/* dark mode */
[data-theme="dark"] {
  --background: #121212;
  --card: #181818;
  --border: #212121;
  --table-header: #212121;
  --primary-text: #f6f6f6;
  --secondary-text: #9ca3af;
}
Enter fullscreen mode Exit fullscreen mode

Now let's add some styles to the checkbox!
the content: "\f186"; will show the moon icon on Font Awesome, which we will be using for dark mode. Similarly, we will use the lightbulb icon for light mode.

#checkbox-theme {
  display: none;
}

/* light mode icon */
#checkbox-theme + label:before {
  content: "\f186";
  font-size: 20px;
  cursor: pointer;
  position: fixed;
  top: 10px;
  right: 10px;
}

/* dark mode icon */
#checkbox-theme:checked + label:before {
  content: "\f0eb";
  font-size: 20px;
  cursor: pointer;
  position: fixed;
  top: 10px;
  right: 10px;
}
Enter fullscreen mode Exit fullscreen mode

And here are some general stylings you can add to your page for testing purposes 😁

body {
  font-family: "Roboto", sans-serif;
  background-color: var(--background);
  color: var(--primary-text);
  text-decoration: none;
}

a {
  text-decoration: none;
  color: var(--primary-text);
}

a:hover {
  text-decoration: none;
}

.wrapper {
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.wrapper h1 {
  font-size: 50px;
}

.wrapper p {
  font-size: 17px;
  margin-top: 0;
}
Enter fullscreen mode Exit fullscreen mode

JavaScript

Now let's write some JavaScript!
The following code will be the main part of our code; It will call the switchTheme function when the user checks/unchecks the checkbox!

Listen for change in checkbox

//listener for changing themes
toggleSwitch.addEventListener('change', switchTheme, false);
Enter fullscreen mode Exit fullscreen mode

We want the switchTheme() function to do 3 main things :

One, Change the data theme to light or dark

document.documentElement.setAttribute('data-theme', 'dark');
Enter fullscreen mode Exit fullscreen mode

Two, Switch the checkbox icon

toggleSwitch.checked = true;
Enter fullscreen mode Exit fullscreen mode

And finally, save the theme to local storage.

localStorage.setItem('theme', 'dark');
Enter fullscreen mode Exit fullscreen mode

Strictly speaking, the third part (localStorage) is not really necessary to achieve this, but we don't want the users to have to switch the theme every time they refresh the website right? So let's utilize localStorage to save the user's preference! We will get back to this on the next part.

Getting back, here is what the complete switchTheme() function looks like :

function switchTheme(e) {
  if (e.target.checked) {
    document.documentElement.setAttribute('data-theme', 'dark');
    toggleSwitch.checked = true;
    localStorage.setItem('theme', 'dark');
  } 
  else {
    document.documentElement.setAttribute('data-theme', 'light');
    toggleSwitch.checked = false;
    localStorage.setItem('theme', 'light');
  }    
}
Enter fullscreen mode Exit fullscreen mode

Saving User's Preference to LocalStorage

Let's get back to LocalStorage. To improve user experience, we want to

  1. Check the user's OS settings to automatically preset the theme
  2. Check localStorage to save user's preference if the user changed the theme

So let's add another function named detectColorScheme() to achieve this!

1. Checking the user's OS settings

This will do the trick!

if(window.matchMedia("(prefers-color-scheme: dark)").matches) {
  //OS theme setting detected as dark
  var theme = "dark";
Enter fullscreen mode Exit fullscreen mode

However, some browsers do not support matchMedia method (Click here to check browser support), so let's consider that as well.

if(!window.matchMedia) {
  // matchMedia method not supported
  return false;
}
else if(window.matchMedia("(prefers-color-scheme: dark)").matches) {
  //OS theme setting detected as dark
  var theme = "dark";
}
Enter fullscreen mode Exit fullscreen mode

So for example, if you are using dark mode on your mac, the website will automatically switch the display to dark mode.

Checking LocalStorage to override OS theme settings

But what if a dark mode user wants to see the website in light mode, and changes it? Let's also save this preference to LocalStorage!

if(localStorage.getItem("theme")){
  if(localStorage.getItem("theme") == "dark"){
    var theme = "dark";
  }
}
Enter fullscreen mode Exit fullscreen mode

Here is the complete code for detectColorScheme()!

function detectColorScheme(){

  //default is set to light mode
  var theme="light"; 

    //local storage is used to override OS theme settings
    if(localStorage.getItem("theme")){
      if(localStorage.getItem("theme") == "dark"){
        var theme = "dark";
            }
        } 
      else if(!window.matchMedia) {
        // matchMedia method not supported
        return false;
      } 
      else if(window.matchMedia("(prefers-color-scheme: dark)").matches) {
        //OS theme setting detected as dark
        var theme = "dark";
        }

        // if dark theme is preferred, set document with a `data-theme==dark` attribute
        if (theme=="dark") {
            document.documentElement.setAttribute("data-theme", "dark");
        }
    }
Enter fullscreen mode Exit fullscreen mode

And here is the complete JS code 🎉

$(document).ready(function () {
  const toggleSwitch = document.querySelector(
    '#theme-switch input[type="checkbox"]'
  );

  function detectColorScheme() {
    var theme = "light"; 

    //local storage is used to override OS theme settings
    if (localStorage.getItem("theme")) {
      if (localStorage.getItem("theme") == "dark") {
        var theme = "dark";
      }
    } else if (!window.matchMedia) {
      return false;
    } else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
      var theme = "dark";
    }

    if (theme == "dark") {
 document.documentElement.setAttribute("data-theme", "dark");
    }
  }
  detectColorScheme();

  function switchTheme(e) {
    if (e.target.checked) {
      localStorage.setItem("theme", "dark");
      document.documentElement.setAttribute("data-theme", "dark");
      toggleSwitch.checked = true;
    } else {
      localStorage.setItem("theme", "light");
      document.documentElement.setAttribute("data-theme", "light");
      toggleSwitch.checked = false;
    }
  }

  toggleSwitch.addEventListener("change", switchTheme, false);

  if (document.documentElement.getAttribute("data-theme") == "dark") {
    toggleSwitch.checked = true;
  }
});
Enter fullscreen mode Exit fullscreen mode

That's it! 🥳

Thanks for reading, and happy coding! ❤️
If you have any thoughts or questions, feel free to leave a comment! 😊

Top comments (0)