DEV Community

Cover image for Implement a Light and Dark Mode Toggle with JavaScript and Local Storage.
AlexDunia
AlexDunia

Posted on

Implement a Light and Dark Mode Toggle with JavaScript and Local Storage.

Introduction

If you want to learn how to toggle between light and dark modes on HTML, CSS, and JAVASCRIPT, then this practical tutorial is for you.

Live project: https://jstogglemode.netlify.app/
Source code: https://github.com/AlexDunia/Js-Toggle-Tech-Writing-source-code-

What is a Toggle/Switcher in this context?

A toggle is a button or icon on a website or app that lets you switch between two options, like showing or hiding a menu, selecting filters, or changing between light and dark mode (Like in our case).

Setting up our Font Awesome Icons to toggle.

To set up this toggle, we need to get the icons for the buttons we want to use.

Here's how the toggle button looks on our website.

Toggle light and dark mode on javascript

Clicking the icons circled red toggles between light and dark modes. We'll import two icons accordingly.

For this project, we will be using Font Awesome.

Sign up to open a new account or log in to continue with the old account.

Once this is done, we will visit our profile.

Toggle light and dark mode on javascript

We want to make use of our kits, so we click on 'Your kits'.

Toggle light and dark mode on javascript

and we click on the alphanumeric string on our profile:

Toggle light and dark mode on Html and css

We will then see the code we need to copy and paste for the icons we will use to toggle to be displayed.

Toggle light and dark mode on javascript

Copy on our HTML

Image description

After this, we go back to the Font Awesome website to search for "Toggle".

We need two icons, toggle on and toggle off.

Image description

Copy and paste that into your HTML too.

Toggle light and dark mode on javascript

Take note of the IDhere, we would be storing them in separate variables and using them as event listeners.

What is Local Storage?

Local Storage is a global object in the browser that lets websites store key/value pairs on a user's device.

What this means on a basic level is that a user can do something on a website, leave that website, restart their computer and that particular thing would remain the same when they get back to it. That's the power of the local storage.

In doing its work, it provides a set of methods (getItem, setItem, removeItem, clear, etc.) to interact with the stored data. You can find out more about local storage here.

In our use case, local storage will be useful in toggling dark and light mode and we begin by storing it inside a variable.

`let mode = localStorage.getItem("mode");`
Enter fullscreen mode Exit fullscreen mode

We're creating a variable to hold our local storage value, ensuring we have a default when the page loads.

We are using let so that its value can be reassigned later in the code.

This 'mode' variable will also help us track the current mode, making it easy to switch between light and dark modes.

If you knew how local storage works before this moment, then the question pops up: why are we using getItem, not setItem? aren't we supposed to initialize it first?

Yes. Either way is right, but in our use case, the trick here is that we are using let mode = localStorage.getItem("mode") to retrieve the value of "mode" from local storage when the page loads.

Setting up our variables

For this project, we need four more variables, making the total 5.

Remember, mode was a variable itself.

  let toggleoff = document.querySelector("#toggleoff");
  let toggleon = document.querySelector("#toggleon");
  let herolarge = document.querySelector("#htx");
  let herosub = document.querySelector("#htxtwo");
  let mode = localStorage.getItem("mode");
Enter fullscreen mode Exit fullscreen mode

Now when we need a function.

I assume you already have the basics of function covered, however, here is a link that explains functions in JavaScript.

Setting up our function.

_Note: For now, we don't have an event listener to listen to the click, and this is intentional.
_

Moving forward, we will create a function called updateMode and pass in a parameter of "mode".

'Mode' is the same name we gave to the first variable we created.

Copy and paste this code into your editor. I will explain each line now.

 function updateMode(mode) {
  if (mode === 'darkmode') {
    localStorage.setItem('mode', 'darkmode');
    toggle.style.display = 'none';
    toggleon.style.display = 'block';
    herolarge.style.color = "white";
    herosub.style.color = "white";
    document.body.classList.add("darkmode");
    document.body.classList.remove('lightmode');
  } else {
    localStorage.setItem('mode', 'lightmode');
    toggle.style.display = 'block';
    toggleon.style.display = 'none';
    herolarge.style.color = "#000039";
    herosub.style.color = "black";
    document.body.classList.remove("darkmode");
    document.body.classList.add('lightmode');
    console.log('light mode');
  }
}
Enter fullscreen mode Exit fullscreen mode

Breaking down our Function.

First, we have a function with a parameter of mode.

This function is used to monitor and change the state of our toggle.

The 'mode' parameter corresponds to the name of our variable that gets the value of the local storage (the first variable we created) because we use the variable as an argument when calling the function.

What you need to understand here is that with the "mode" parameter, the function is aware of the current state stored in local storage.

Then, we have a condition:

if (mode === 'darkmode')
Enter fullscreen mode Exit fullscreen mode

When we check if (mode === 'darkmode'), it means we are verifying whether our variable (mode), which is getting its value from local storage, is equal to 'darkmode'.

So what happens if it is equal to darkmode?

Remember, in the first instance we haven't explicitly set any local storage values yet, we used a getItem instead.

Now, Inside this condition, we'll be setting up our local storage for the first time:

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

This sets our value to "darkmode".

Our function has a condition that if our mode is darkmode, then we set a local storage called 'darkmode'.

After that, we move to these lines:

toggle.style.display = 'none';
toggleon.style.display = 'block';
Enter fullscreen mode Exit fullscreen mode

These are the variables that control our Font Awesome icons.

When it is dark mode, our toggle button or toggle icon activates and needs to be 'on'.

Let's take a step back to properly understand.

Note: We are trying to control the dark mode here.

By default, our toggle is 'off.'

It is 'off' because our dark mode is off.

The toggle controls our dark mode.

When we click the button, the dark mode comes 'on', and so does our button.

This works vice versa because when the toggle is off, then our dark mode also goes off.

Turning on the night mode means showing the Font Awesome icon that displays the toggle "on."

In essence, the objective is to set the default state of the button as 'OFF.' When clicked, it toggles to 'ON,' activating the night mode and displaying the corresponding Font Awesome icon for the 'ON' state

If that is clear then we move to the next two lines:

 document.body.classList.add("darkmode");
 document.body.classList.remove('lightmode');
Enter fullscreen mode Exit fullscreen mode

If we refer back to our CSS, specifically lines 38-44, we have two classes: one sets the background color to light mode (white), and the other sets it to dark mode (ash).

.lightmode{
    background-color: white;
}

.darkmode{
    background:#1b1b1b;
}
Enter fullscreen mode Exit fullscreen mode

On our CSS, we also have the body:

body{
    font-family:Montserrat;
    overflow-x: hidden;
    position: relative;
}
Enter fullscreen mode Exit fullscreen mode

On the hero text and subtext, we have default colors(lines 49 and 54 specifically), this shows we can do it however we want to, provided there is a color.

 #htx {
        animation: fadeIn 1s ease-in-out 1.5s forwards;
        line-height:1.1em;
        color:#000039;
    }

    #htxtwo {
        animation: fadeIn 1s ease-in-out 2.5s forwards;
        color: black;
        margin-top:15px;
        font-weight:500;
    }
Enter fullscreen mode Exit fullscreen mode

The JavaScript code adds the appropriate background color and text color based on the selected mode.

The first half of our updateMode condition checks if the mode set in our local storage is darkmode.

If our stored mode is set to darkmode, we do the following:

  • Save dark mode in the storage,

  • Switch the icon from 'off' to 'on'.

  • Make the page dark.

  • Remove any light styles.

The terms 'dark mode' and 'light mode' here correspond to the CSS classes we have defined. The function updates the styles and adds them to the body of our website based on the selected mode.

Note: darkmode and lightmode are not prefixed names. You can name them anything you choose.

 function updateMode(mode) {
  if (mode === 'darkmode') {
    localStorage.setItem('mode', 'darkmode');
    toggleoff.style.display = 'none';
    toggleon.style.display = 'block';
    document.body.classList.add("darkmode");
    herolarge.style.color = "white";
    herosub.style.color = "white";
    document.body.classList.remove('lightmode');
    console.log('dark mode');
  } else {
    localStorage.setItem('mode', 'lightmode');
    toggleoff.style.display = 'block';
    toggleon.style.display = 'none';
    herolarge.style.color = "#000039";
    herosub.style.color = "black";
    document.body.classList.remove("darkmode");
    document.body.classList.add('lightmode');
    console.log('light mode');
  }
}
Enter fullscreen mode Exit fullscreen mode

For the else condition, we reverse everything.

else{ 
    localStorage.setItem('mode', 'lightmode');
    toggleoff.style.display = 'block';
    toggleon.style.display = 'none';
    herolarge.style.color = "#000039";
    herosub.style.color = "black";
    document.body.classList.remove("darkmode");
    document.body.classList.add('lightmode');
    console.log('light mode');
}
Enter fullscreen mode Exit fullscreen mode

If the mode is not darkmode, then we do the opposite: show the 'off' icon, hide the 'on' icon, switch the page to light mode, and remove any darkmode styles.

Now our function is ready to go, but it's not done yet.

We need two event listeners that listen for a click event outside the function.

This will be the toggle on and toggle off event.

Without these, our well-written function won't do anything.

That listener calls the function.

toggleon.addEventListener('click', function () {
  updateMode('lightmode');
});

toggleoff.addEventListener('click', function () {
  updateMode('darkmode');
});
Enter fullscreen mode Exit fullscreen mode

When we click on the toggleon, it triggers the updateMode function with a parameter of lightmode.

This implies that whenever we click on either the 'toggleon' or 'toggleoff' icon, we invoke the function to perform different actions.

Toggle light and dark mode on HTML, CSS and JAVASCRIPT

and for the final piece of the puzzle:

if (mode === 'darkmode') {
  updateMode('darkmode');
} else if (mode === 'lightmode'){
  updateMode('lightmode');
} else {
  updateMode('lightmode');
}
Enter fullscreen mode Exit fullscreen mode

If the local storage mode is 'darkmode,' update to dark mode.

If the mode is 'lightmode,' update to light mode.

The additional condition is our DEFAULT STATE, and it handles situations where no mode is stored in local storage, which often occurs on page load. In such cases, the default action is to set the mode to 'lightmode,' ensuring the page loads in light mode by default.

This code helps maintain the user's preferred mode across page loads.

CONCLUSION

Adding beauty to the user interface (UI) via interaction is crucial because users find it visually appealing.  Also, it is a good trick to learn as it gives beginners a friendly approach to logic in JavaScript and DOM manipulation.

Thanks for stopping by! If you found this helpful, consider giving it a like, dropping a comment, and hitting that follow button.

Top comments (0)