Create a Dark/Light Mode Toggle using JavaScript & LocalStorage 💫
Tags: tutorial, webdev, beginners, javascript
Published: 2023-07-23 03:25:36

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!

{% codepen https://codepen.io/quicksilversel/pen/abQGKZL %}
(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

```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>
```

We will be using the checkbox property and [Font Awesome Icons](https://fontawesome.com/) 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!

```CSS
/* 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;
}
```
Now let's add some styles to the checkbox!
the `content: "\f186";` will show the [moon icon on Font Awesome](https://fontawesome.com/icons/moon?f=classic&s=solid), which we will be using for dark mode. Similarly, we will use the [lightbulb icon](https://fontawesome.com/icons/lightbulb?f=classic&s=solid) for light mode.

```CSS
#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;
}
```
And here are some general stylings you can add to your page for testing purposes 😁
```CSS
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;
}
```

## 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

```JS
//listener for changing themes
toggleSwitch.addEventListener('change', switchTheme, false);
```

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

One, Change the data theme to light or dark
```JS
document.documentElement.setAttribute('data-theme', 'dark');
```
Two, Switch the checkbox icon 
```JS
toggleSwitch.checked = true;
```
And finally, save the theme to local storage.
```JS
localStorage.setItem('theme', 'dark');
```

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 :

```JS
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');
  }    
}
```

### 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! 
```JS
if(window.matchMedia("(prefers-color-scheme: dark)").matches) {
  //OS theme setting detected as dark
  var theme = "dark";
```
However, some browsers do not support matchMedia method ([Click here](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia) to check browser support), so let's consider that as well.

```JS
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";
}
```
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!

```JS
if(localStorage.getItem("theme")){
  if(localStorage.getItem("theme") == "dark"){
    var theme = "dark";
  }
}
```

Here is the complete code for `detectColorScheme()`!
```JS
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");
        }
    }
```

And here is the complete JS code 🎉

```JS
$(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;
  }
});
```

### That's it! 🥳

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