Build a Theme Switcher for Your Website with JavaScript

Build a Theme Switcher for Your Website with JavaScript

You might have noticed that nowadays, many websites have a theme switcher on top of their website. The theme switcher can also be used as a dark mode switcher. In this article, we’ll be building a theme switcher like that.

I am not going to focus a lot on the styles; instead, we’ll try to understand the logic about creating it. Also, the theme switcher will be persistent. The selected theme will be stored in the browser and will apply the same theme even after reloading. We’ll implement this using the browser’s local storage. So, let’s start building.

Things you need to know

  • Basic HTML - Learn HTML
  • Basic CSS - Learn CSS
  • Little JavaScript - Learn JavaScript and you are good to go.

Here's our Complete App

Complete Theme Switcher One thing I must explain before we start building the Markup is custom data attributes in HTML.

What are HTML Data Attributes?

There may be situations when we have to store some information associated with DOM elements. Let's take an example, suppose we have a list of employees and we want to save their IDs, which we can use to manipulate them using the DOM. Before HTML5, we could have used class or IDs, but this is not an impressive solution. HTML5 introduced us to the concept of Custom Data Attributes. Any attribute that starts with data- is a custom data attribute. We also have special HTML data tags in HTML5.

For example, we can name data-identity to create a custom data attribute for employee ID. Two things to keep in mind while creating a data attribute is that the value that is stored inside data attributes can only be of string type. And the second point is that we cannot use any valid HTML attribute as a name for data attributes. Like, data-id is not appropriate.

We can access these attributes in JavaScript with dataset property.

Suppose we want to access the identity attribute from before. We can access this using the below codes,

let employee = document.getElementByClassName("employee-table");
let empID = employee.dataset.identity;
// Or
let empID = employee.dataset['identity']

Now that you have a basic understanding of the data attributes, let's start building our Theme Switcher.

File Structure:

|--themes
|------dark.css
|------light.css
|------purple.css
|------sky.css
|--index.html
|--script.js
|--style.css

Now that we know the file structure, let's begin the coding.

Building the Markup

As you can understand from the gif above, we are not using any high-level design. We have a simple header section with the name of our app and the buttons to change the themes. Our body contains an image and some random texts. We are also using the Roboto font to style the document.

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Theme Switcher</title>
    <link rel="stylesheet" href="./style.css">
    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700;900&display=swap" rel="stylesheet">
    <link rel="stylesheet" id="switcher-id" href="">
</head>
<body>
    <header>
        <h1>???? THEME SWITCHER</h1>
        <div class="theme-switches">

            <div data-theme="light" class="switch" id="switch-1"></div>

            <div data-theme="sky" class="switch" id="switch-2"></div>

            <div data-theme="purple" class="switch" id="switch-3"></div>

            <div data-theme="dark" class="switch" id="switch-4"></div>

        </div>
    </header>

    <div class="container">
        <div class="box">
            <img src="https://images.unsplash.com/photo-1597926588114-2d9c1190b5c7?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=923&q=80"
                alt="Placeholder" class="image">
            <div class="text">
                <h3>A Sweet Heading</h3>
                <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod 
                tempor incididunt ut labore et dolore magna aliqua. Ut enim 
                ad minim veniam, quis nostrud exercitation ullamco laboris 
                nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor 
                in reprehenderit in voluptate velit esse cillum dolore eu fugiat 
                nulla pariatur. Excepteur sint occaecat cupidatat non proident, 
                sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
            </div>
        </div>
    </div>
    <script src="./script.js"></script>

</body>

</html>

That is just a few lines of Markup. We have added the fonts in the header, default styles stylesheets, and a stylesheet link with a blank href. This blank href will be used to attach our theme designs. You can also see we are using a data-theme custom data attribute to keep track of the theme selected. We will use the IDs of theme switches to design the switches. In our main body, we are using an image from Unsplash. And some random text. And finally, in the end, we are linking our script file.

Styling Our Default CSS

As you might have observed, we have two stylesheets tag in our HTML document. The first stylesheet is responsible for all the default styles our website will have, like the font-sizes, viewport height-width, image sizes. And the second one will be responsible for the specific theme designs.

We’ll also use CSS variables in this example. The benefits of using variables are that we can declare a variable once and use it multiple times in a document. Declaring variables in CSS are pretty easy. We usually define them in the :root selector. Any name that starts with -- is a CSS variable. Example,

root {
    --text-color: #000;
    --back-color: #eee;
}

In our default stylesheet, we will be declaring four variables that will be used for the theme switches. Then we'll reset our document. We'll use flex-box to design the theme switches. The display:flex property set on the parent element will make the child elements horizontal. The justify-content:center will center the elements. For the growing effect on the switches, we are using the CSS scale transform property. We are scaling the element 1.2 times the original. And for the smooth effect, we are using transition: 0.3s all;. Then, we are using our variables to design the switches. We are adding the variables as background-color on the switches to design them. You can check the code, and you'll get a clear idea. We are also using some basic media-queries for responsiveness. I think I don't have to explain the whole code because it is pretty basic. And I've discussed the portions I thought needs some explanation. Here's the full CSS code.

:root {
  --light: #ffffff;
  --sky: #7f9cf5;
  --purple: #97266d;
  --dark: #81899b;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: 'Roboto', sans-serif;
  height: 100vh;
  overflow-x: hidden;
}

header {
  text-align: center;
  font-size: 30px;
  padding: 50px 0;
}

.theme-switches {
  display: flex;
  justify-content: center;
}

.switch {
  border: 2px solid black;
  border-radius: 50px;
  height: 30px;
  width: 30px;
  margin: 10px;
  cursor: pointer;
}

.switch:hover {
  transform: scale(1.2);
  transition: 0.3s ease-in-out;
}

#switch-1 {
  background-color: var(--light);
}

#switch-2 {
  background-color: var(--sky);
}

#switch-3 {
  background-color: var(--purple);
}

#switch-4 {
  background-color: var(--dark);
}

.container {
  max-width: 80%;
  margin: 0 auto;
}

.box {
  display: flex;
  justify-content: space-around;
  margin-top: 100px;
}

.box img {
  max-height: 300px;
  width: auto;
  border-radius: 20px;
  margin: 20px;
}

.text {
  width: 50%;
}

.text h3 {
  font-size: 35px;
  padding: 30px 0;
  font-weight: 600;
}

.text p {
  font-size: 20px;
  font-weight: 400;
}

/* Media Query */
@media (max-width: 768px) {
  header h1 {
    font-size: 25px;
  }

  .box {
    display: flex;
    flex-wrap: wrap;
  }

  .box img {
    max-width: 80vw;
  }

  .text {
    width: 80%;
  }
}

Styling Our Themes

Now, let's set our theme files.

First, let’s understand the properties that change when we change a theme. Let's make a list of them.

  1. Heading Background
  2. Text Colors
  3. Image Border
  4. Body Background Color Now that we've outlined the colors we’ll be changing, most of our work is done. First, create a file called light.css for the light styles.

light.css

:root {
  --back-color: #eee;
  --primary-color: #000000;
  --header-back: #5d5d5d;
  --header-text: #eee;
}

body {
  background-color: var(--back-color);
  color: var(--primary-color);
}

header {
  background-color: var(--header-back);
  color: var(--header-text);
}

.box img {
  border: 2px solid var(--primary-color);
}

As you can see, we've defined the colors we'll be using in the light theme as variables. Then we are just adding the colors to the properties we want to change. Pretty simple, right? I am attaching the other theme files below,

sky.css

:root {
  --back-color: #c3dafe;
  --primary-color: #3c366b;
  --header-back: #5f718d;
  --header-text: #eee;
}

body {
  background-color: var(--back-color);
  color: var(--primary-color);
}

header {
  background-color: var(--header-back);
  color: var(--header-text);
}

.box img {
  border: 2px solid var(--primary-color);
}

purple.css

:root {
  --back-color: #fed7e2;
  --primary-color: #702459;
  --header-back: #874f5e;
  --header-text: #eee;
}

body {
  background-color: var(--back-color);
  color: var(--primary-color);
}

header {
  background-color: var(--header-back);
  color: var(--header-text);
}

.box img {
  border: 2px solid var(--primary-color);
}

dark.css

:root {
  --back-color: #81899b;
  --primary-color: #f9ffee;
  --header-back: #1a1d24;
  --header-text: #eee;
}

body {
  background-color: var(--back-color);
  color: var(--primary-color);
}

header {
  background-color: var(--header-back);
  color: var(--header-text);
}

.box img {
  border: 2px solid var(--primary-color);
}

As you can see, all our themes have the same styles, we’re just changing the color variables, and everything is ready.

The JavaScript Part

Here comes the most crucial part. Our JavaScript code is responsible for all the theme-changing things. Let’s first understand the logic behind our script file.

First, we’ll loop through all the switches and store the dataset value into a variable. Then, we’ll pass the value into a function that will set the theme accordingly.

We’ll get all the switches used in HTML using their class name and store those into a variable called switches.

let switches = document.getElementsByClassName('switch');

Then we’ll loop over the switches and will listen for the click event on the switches. We’ll store the dataset value clicked inside a variable called theme in our case. I’ll be using a for...in loop here. If you are not familiar with for...in loop, check this article.

for (let i of switches) {
  i.addEventListener('click', function () {
    let theme = this.dataset.theme;
    console.log(theme);
  });
}

For your understanding, I’m first console logging the value that is clicked. Every time we click on a switch, it’ll console.log its custom data attribute value. You can see the gif to get an idea.

Console Logging Values

Now we have to create a function called setTheme, which will set our theme. Let's see how it looks,

function setTheme(theme) {
  if (theme == 'light') {
    document.getElementById('switcher-id').href = './themes/light.css';
  } else if (theme == 'sky') {
    document.getElementById('switcher-id').href = './themes/sky.css';
  } else if (theme == 'purple') {
    document.getElementById('switcher-id').href = './themes/purple.css';
  } else if (theme == 'dark') {
    document.getElementById('switcher-id').href = './themes/dark.css';
  }
}

We are passing the parameter which we got from the dataset into this function. We can also achieve the same with a switch case statement. Our blank stylesheet in the HTML has the switcher-id id in it. We are targetting that element and adding an href to it. We are keeping all our theme styles in a separate folder called themes. And we have to call this function from the loop. So, our final code for the loops will be,

for (let i of switches) {
  i.addEventListener('click', function () {
    let theme = this.dataset.theme;
    console.log(theme);
    setTheme(theme);
  });
}

I think it's making sense now. We are almost done. The final thing is to implement the local storage here. The stored data in local storage is saved across browser sessions, and it is pretty similar to session storage. Though, the significant difference between these is, local storage does not have any expiry. Local storage creates a key-value pair to store data.

The localStorage.getItem, with a name parameter, will create a key to save our values. In our example, we’ll be using a key called theme. So, our first step is to initiate it.

let style = localStorage.getItem('style');

The default property of a local storage element is null. So, our next step is to check if it’s null and if it is, we will set a theme value for it.

if (style == null) {
    setTheme('light');
} else {
    setTheme(style);
}

Here, if the value is null, we are setting the light theme as default. Otherwise, it'll set the value that is stored in the local storage. And finally, We have to set the value as per the clicked switch. We’ll achieve this using the setItem property of local storage.

localStorage.setItem('style', theme);

We’ll add this code before the end of the setTheme function. So, our complete script.js file will look like this,

let switches = document.getElementsByClassName('switch');

let style = localStorage.getItem('style');

if (style == null) {
  setTheme('light');
} else {
  setTheme(style);
}

for (let i of switches) {
  i.addEventListener('click', function () {
    let theme = this.dataset.theme;
    setTheme(theme);
  });
}

function setTheme(theme) {
  if (theme == 'light') {
    document.getElementById('switcher-id').href = './themes/light.css';
  } else if (theme == 'sky') {
    document.getElementById('switcher-id').href = './themes/sky.css';
  } else if (theme == 'purple') {
    document.getElementById('switcher-id').href = './themes/purple.css';
  } else if (theme == 'dark') {
    document.getElementById('switcher-id').href = './themes/dark.css';
  }
  localStorage.setItem('style', theme);
}

To learn more about the local storage web API, check this MDN article. You can see the value of local storage from the Inspect-->Application-->Local Storage. We can check the gif below to see how it changes. Theme Changing

I hoped you learned something new in this article. Feel free to comment on your thoughts. And you can find the complete source code here.

Did you find this article valuable?

Support Nemo by becoming a sponsor. Any amount is appreciated!