Create a Theme Switcher with HTML, CSS, and JavaScript

Stacky Team
Stacky Team
---

Introduction

In today's digital landscape, user experience plays a pivotal role in the success of any website or application. One crucial aspect of user experience is providing an option for users to switch between light and dark themes. This article will guide you through creating a sophisticated theme switcher using HTML, CSS, and JavaScript, leveraging localStorage to remember the user's preference.

Prerequisites

Before we begin, ensure you have basic knowledge of HTML, CSS, and JavaScript. This guide assumes you are familiar with these technologies.

Steps to Create the Theme Switcher

1. HTML Structure

First, we need to create the HTML structure for our theme switcher. This includes a set of radio buttons for selecting the theme.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Theme Switcher</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="theme-switcher">
        <label>
            <input type="radio" name="theme" value="system" checked>
            System default
        </label>
        <label>
            <input type="radio" name="theme" value="light">
            Light
        </label>
        <label>
            <input type="radio" name="theme" value="dark">
            Dark
        </label>
    </div>
    <script src="script.js"></script>
</body>
</html>

2. CSS Styling

Next, we will style our theme switcher and define the light and dark themes.

/* styles.css */
body {
    transition: background-color 0.3s, color 0.3s;
}

.theme-switcher {
    display: flex;
    flex-direction: column;
    padding: 20px;
}

label {
    margin-bottom: 10px;
}

input[type="radio"] {
    margin-right: 10px;
}

/* Light theme */
body.light-theme {
    background-color: #ffffff;
    color: #000000;
}

/* Dark theme */
body.dark-theme {
    background-color: #000000;
    color: #ffffff;
}

3. JavaScript Logic

Finally, we will write the JavaScript to handle theme switching and persist the user's preference using localStorage.

// script.js
class ThemeSwitcher {
    constructor() {
        this.themeOptions = document.querySelectorAll('input[name="theme"]');
        this.currentTheme = localStorage.getItem('theme') || 'system';
        this.prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');

        this.init();
    }

    init() {
        this.applyTheme(this.currentTheme);
        this.addEventListeners();
        this.syncWithSystemPreference();
    }

    applyTheme(theme) {
        if (theme === 'system') {
            if (this.prefersDarkScheme.matches) {
                this.setDarkTheme();
            } else {
                this.setLightTheme();
            }
        } else if (theme === 'light') {
            this.setLightTheme();
        } else if (theme === 'dark') {
            this.setDarkTheme();
        }
    }

    setLightTheme() {
        document.body.classList.add('light-theme');
        document.body.classList.remove('dark-theme');
    }

    setDarkTheme() {
        document.body.classList.add('dark-theme');
        document.body.classList.remove('light-theme');
    }

    addEventListeners() {
        this.themeOptions.forEach(option => {
            if (option.value === this.currentTheme) {
                option.checked = true;
            }
            option.addEventListener('change', (event) => {
                this.handleThemeChange(event.target.value);
            });
        });

        this.prefersDarkScheme.addEventListener('change', () => {
            if (localStorage.getItem('theme') === 'system') {
                this.applyTheme('system');
            }
        });
    }

    handleThemeChange(theme) {
        localStorage.setItem('theme', theme);
        this.applyTheme(theme);
    }

    syncWithSystemPreference() {
        if (this.currentTheme === 'system') {
            this.applyTheme('system');
        }
    }
}

new ThemeSwitcher();

Detailed Explanation

  1. Class-Based Approach: We encapsulate our logic within a ThemeSwitcher class to keep our code modular and maintainable.
  2. Initialization: The init method initializes the theme switcher by applying the current theme and adding event listeners.
  3. Theme Application: The applyTheme method determines the correct theme to apply based on the user's preference or system settings.
  4. Event Handling: Event listeners are added to the radio buttons to handle changes and update the theme accordingly. We also listen for system preference changes to sync the theme dynamically.
  5. Persisting User Preference: The handleThemeChange method stores the selected theme in localStorage, ensuring the preference is remembered across sessions.

Important Notes

  • Note: Ensure you include the CSS file (styles.css) and JavaScript file (script.js) in the same directory as your HTML file.
  • Warning: Placing the <script> tag at the end of the body ensures that the script runs after all the DOM elements are fully loaded. This avoids issues where the script tries to access elements that haven't been loaded yet.

Conclusion

By following the steps outlined above, you can create a sophisticated theme switcher that enhances the user experience of your website or application. This theme switcher leverages modern JavaScript practices and ensures user preferences are respected and persisted.

Feel free to customize the styles and functionality to better fit your needs. Happy coding!