# 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
![](https://i.ibb.co/KwjRQq8/ezgif-com-video-to-gif.gif)*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,
```javascript
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:**
```sh
|--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.
```html
<!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,
```css
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.
```css
: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**
```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**
```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**
```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**
```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`.

```javascript
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](https://www.studytonight.com/javascript/javascript-for-loop).

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

![](https://i.ibb.co/M1HHCrQ/ezgif-com-crop.gif)*Console Logging Values*

Now we have to create a function called `setTheme`, which will set our theme. Let's see how it looks,
```javascript
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,
```javascript
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.
```javascript
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.
```javascript
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.
```javascript
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,

```javascript
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](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) 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.
![](https://i.ibb.co/pfkwtKd/ezgif-com-crop-1.gif)*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](https://github.com/nemo0/JS-Theme-Switcher).

