1. Web Design
  2. HTML/CSS
  3. JavaScript for Designers

Switch Between Color Schemes with CSS Variables and JavaScript

Scroll to top

In this tutorial, we’ll discuss how to implement different color schemes on a website using CSS variables and one line of vanilla JavaScript.

First, we’ll implement a simple light/dark mode toggle switch. Then we’ll expand on that to implement as many themes as we’d like; light mode, dark mode, 90’s neon mode, you name it!

We’ll also use media queries to display dark or light mode depending on a user’s preferences, and local storage to remember which of our color schemes they’ve selected.

Light/Dark Mode Toggle Switch Demo

Let’s take a look at the demo for a light/dark mode toggle switch. We’ll be building this first, then expanding on it.

1. How to Create a Dark Mode Toggle

HTML

First, we’ll initialise two buttons to allow us control light and dark mode. 

1
<div class="toggle-container">
2
    <button class="theme-btn light" title="Light mode">
3
    <!--sun icon-->
4
    </button>
5
    <button class="theme-btn dark" title="Dark mode">
6
    <!--moon icon-->
7
    </button>
8
    </div>

I used these weather icons from Envato Elements for the sun and moon to represent light and dark themes, but you can replace them with whatever content you prefer. I also set the title attribute to provide context for screen readers.

Weather iconsWeather iconsWeather icons

CSS

Most of the work with our toggle implementation is done using CSS. We can style the buttons to achieve the rounded icon effect:

1
.theme-btn {
2
    width: 3em;
3
    height: 3em;
4
    padding: 0.5em;
5
    border-radius: 50%;
6
    cursor: pointer;
7
    border: none;
8
    background-color: transparent;
9
    }
10
11
    .theme-btn img {
12
    height: 100%;
13
    width: 100%;
14
    object-fit: cover;
15
    }

We define our theme colors using CSS custom properties.

“Custom properties [...] contain specific values to be reused throughout a document. They are set using custom property notation (e.g., --main-color: black;) and are accessed using the var() function (e.g., color: var(--main-color);)” - MDN

Custom properties are usually defined inside the :root pseudo-class, which allows us to target the <html> element tag. In this example, we defined the variables for a default, light and dark theme for the :root using class names.

1
:root,
2
    :root.light {
3
    --bg-color: #fff;
4
    --text-color: #123;
5
    }
6
7
    :root.dark {
8
    --bg-color: #121212;
9
    --text-color: #45ADFE;
10
    }

Now we’ll assign our custom properties to other elements–this is what allows the colors to change depending on the :root class. We can assign colors to the body like this:

1
body {
2
    background-color: var(--bg-color);
3
    color: var(--text-color);
4
    }
5
  

The body tag returns the selected variable color depending on the class assigned to the :root

JavaScript

At this point we can use JavaScript to determine the class for the :root tag and, as promised, we’ll only need one line:

1
const setTheme = theme => document.documentElement.className = theme;

The setTheme function sets the root element’s class to the parameter theme.

We can pass this function to our buttons so the updated HTML looks like this:

1
 <div class="toggle-container">
2
    <button class="theme-btn light" onclick="setTheme('light')" title="Light mode">
3
    <!--sun icon-->
4
    </button>
5
    <button class="theme-btn dark" onclick="setTheme('dark')" title="Dark mode">
6
    <!--moon icon-->
7
    </button>
8
    </div>

Clicking the light button sets the HTML class to light and clicking the dark button sets the HTML class to dark.

Here’s what happens to our HTML element depending on the button clicked:

html class changes from 'dark' to 'light'html class changes from 'dark' to 'light'html class changes from 'dark' to 'light'

And the style for the body tag looks like this:

toggling colors for the body tagtoggling colors for the body tagtoggling colors for the body tag

With this, we’ve implemented everything we need to switch between a light and dark theme!

2. Handling Styling Based on Color Theme

Thanks to this implementation, we can choose to style elements differently based on which class name the HTML element has. In our demo, we only display one button at a time so we can hide the other button depending on the chosen class. Here’s what the CSS looks like:

1
.theme-btn.light {
2
    display: none;
3
    }
4
5
    .dark .theme-btn.light {
6
    display: block;
7
    background-color: #fff;
8
    }
9
10
    .dark .theme-btn.dark {
11
    display: none;
12
    }

With this, we show the light button and hide the dark button when the parent element has a class dark.

3. More Custom Properties

CSS custom properties are not just limited to colors, we can change everything from font size to background images. Here’s a demo where we change the font style and background image using the toggle switch.

Images used: Young woman with curly hair smiling and Millennial group of friends lighting sparklers at night.

We only need to update the CSS so it looks like:

1
:root,
2
    :root.light {
3
    --bg-color: #d6e8f5;
4
    --text-color: #0a1c29;
5
    --bg-url: url(//image-url);
6
    --font-family: 'Fredoka One', cursive;
7
    }
8
9
    :root.dark {
10
    --bg-color: #0a1c29;
11
    --text-color: #45ADFE;
12
    --bg-url: url(//image-url);
13
    --font-family: 'Alfa Slab One', cursive;
14
    }
15
16
    body {
17
    background-image: var(--bg-url);
18
    background-size: cover;
19
    color: var(--text-color);
20
    font-family: var(--font-family);
21
    }

4. Multiple Color Schemes

Now that we’ve implemented the light/dark mode toggle, we can go ahead to expand on that to create as many color schemes as we’d like.

For this method, we’ve created a dropdown that allows you select 6 different themes for your page. Take a look at the demo:

HTML

In this example, we use a select element to create the dropdown and pass the themes into the select as options. The values of the options are what we’ll be passing to the HTML element as classes.

1
<select name="theme-select" id="theme-select">
2
    <option value="light">Light</option>
3
    <option value="dark">Dark</option>
4
    <option value="blue">Blue</option>
5
    <option value="pink">Pink</option>
6
    <option value="space">Space</option>
7
    <option value="nyan">Nyan</option>
8
    </select>

CSS

We’ll create our :root variables for the different themes using class names.

1
:root,
2
    :root.light {
3
    --bg-color: #fff;
4
    --text-color: #123;
5
    }
6
7
    :root.dark {
8
    --bg-color: #121212;
9
    --text-color: #696d7d;
10
    }
11
12
    :root.blue {
13
    --bg-color: #05396B;
14
    --text-color: #E7F1FE;
15
    }
16
17
    :root.pink {
18
    --bg-color: #ffcad4;
19
    --text-color: #e75480;
20
    }
21
22
    :root.space {
23
    --bg-color: #000;
24
    --text-color: #f2bd16;
25
    --bg-url: url(//space);
26
    --font-family: 'Press Start 2P', cursive;
27
    }
28
29
    :root.nyan {
30
    --bg-color: #013367;
31
    --text-color: #fff;
32
    --bg-url: url(//nyan);
33
    --font-family: 'Comic Neue', cursive;
34
    }

We can also style the select tag to use the variables instead of default styling:

1
select {
2
    padding: 0.25rem 0.75rem;
3
    font-size: 1.25rem;
4
    font-family: inherit;
5
    background-color: var(--bg-color);
6
    color: var(--text-color);
7
    border-radius: 0.25rem;
8
    }
9
  

Then our body style looks like this:

1
body {
2
    margin: 0;
3
    background-color: var(--bg-color);
4
    background-image: var(--bg-url);
5
    color: var(--text-color);
6
    font-family: var(--font-family, "Open Sans", sans-serif);
7
    }

Note: we can also pass fallback values into CSS variables, so setting the property font-family: var(--font-family, "Open Sans", sans-serif allows the browser to set the font-family property as "Open Sans", sans-serif if the CSS variable --font-family is undefined.

JavaScript

Finally, we update our JavaScript to detect when an option has been chosen from the select element.

1
const setTheme = theme => document.documentElement.className = theme;
2
3
    document.getElementById('theme-select').addEventListener('change', function() {
4
    setTheme(this.value);
5
    });

We target the select element and pass the selected value in setTheme as the root class.

That’s all the JavaScript we need and we can go ahead to have as many themes as we’d like by including more HTML options and :root styles in the CSS.

5. Setting Theme According to User Preferences

We can also decide whether to set a light or a dark mode based on the user system’s preferences. We can detect a user’s preferred color scheme by using the prefers-color-scheme media query .

“The prefers-color-scheme CSS media feature is used to detect if the user has requested a light or dark color theme.” - MDN

1
@media (prefers-color-scheme: dark) {
2
    :root {
3
    --bg-color: #121212;
4
    --text-color: #45ADFE;
5
    }
6
    }

In the media query, we set our default :root variables to the colors for our dark mode if the user’s system is set to a dark theme.

6. Store User Choice in Local Storage

Another feature is storing a user’s selected theme in local storage so the page takes the chosen theme by default every time they visit. We can modify our setTheme function to store the selected option in local storage.

1
const setTheme = (theme) => {
2
    document.documentElement.className = theme;
3
    localStorage.setItem('theme', theme);
4
    }

To load the saved theme when the page loads, we can create a getTheme function that gets the theme item from localStorage and call the function whenever the script is loaded:

1
const getTheme = () => {
2
    const theme = localStorage.getItem('theme');
3
    theme && setTheme(theme);
4
    }
5
6
    getTheme();

Conclusion

To summarize, we can create a theme selector by:

  1. Setting theme colors for the :root using CSS variables
  2. Using JavaScript to change the root class.
  3. Calling the function in the corresponding HTML element.

With this, we’ve created a fully functional theme selector that saves user options and takes their preferences into consideration.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Web Design tutorials. Never miss out on learning about the next big thing.
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.