CSS Theming Using Variables and JavaScript

Introduction

In the evolving landscape of web development, the demand for personalized and adaptable user interfaces is higher than ever. CSS theming allows developers to create dynamic and flexible themes, ensuring a consistent and visually appealing experience across different platforms and devices. By utilizing CSS variables (custom properties) in conjunction with JavaScript, we can create themes that are not only reusable but also capable of adapting to user preferences in real time.

In this comprehensive guide, we'll dive deep into CSS theming, starting with the basics of CSS variables, moving on to creating and switching themes using JavaScript, and finally exploring advanced theming concepts such as context-aware theming, dynamic theme generation, and user preference storage.

Understanding CSS Variables
 

What Are CSS Variables?

CSS variables, also known as custom properties, allow you to store values in a central location and reuse them throughout your stylesheet. They provide a powerful way to manage and maintain styles, particularly when dealing with complex themes.

Example. Basic CSS Variables

:root {
  --primary-color: #3498db;
  --secondary-color: #2ecc71;
  --font-size: 16px;
}

body {
  color: var(--primary-color);
  font-size: var(--font-size);
}

button {
  background-color: var(--secondary-color);
  color: #fff;
  padding: 10px 20px;
  border: none;
  border-radius: 5px;
}

In the above example, variables like --primary-color and --secondary-color are defined in the :root selector, making them accessible throughout the document. The var() function is used to retrieve the value of these variables wherever needed.

Benefits of Using CSS Variables

  • Reusability: Define once, use everywhere.
  • Maintainability: Easy to update and maintain.
  • Dynamic Styling: This can be manipulated with JavaScript to create dynamic themes.
  • Scope and Inheritance: CSS variables can be scoped to specific elements and inherit values from parent elements.

Creating Basic Themes
 

Step 1. Defining Themes

Let's start by creating two basic themes: a light theme and a dark theme. We'll define these themes using CSS variables in the :root selector for the light theme and a separate class for the dark theme.

Light Theme

:root {
  --background-color: #ffffff;
  --text-color: #000000;
  --button-background: #007bff;
  --button-text-color: #ffffff;
}

body {
  background-color: var(--background-color);
  color: var(--text-color);
}

button {
  background-color: var(--button-background);
  color: var(--button-text-color);
  padding: 10px 20px;
  border: none;
  border-radius: 5px;
}

Dark Theme

.dark-theme {
  --background-color: #333333;
  --text-color: #ffffff;
  --button-background: #ff5722;
  --button-text-color: #000000;
}

In the dark theme, we override the default values set in the :root with darker tones suitable for low-light environments.

Step 2. Applying Themes with JavaScript

To allow users to switch between themes dynamically, we'll use JavaScript to toggle the dark theme class on the body element.

JavaScript for Theme Switching

document.getElementById('theme-switcher').addEventListener('click', function () {
  document.body.classList.toggle('dark-theme');
});

Each time the button is clicked, the dark-theme class is toggled on or off, switching between the light and dark themes.

Advanced Theming Concepts
 

1. Context-Aware Theming

Context-aware theming refers to the ability of a theme to adapt based on the context in which it is used. This could include changes based on time of day, user preferences, or even specific content types.

Example. Time-Based Theming

const hour = new Date().getHours();

if (hour >= 18 || hour < 6) {
  document.body.classList.add('dark-theme');
} else {
  document.body.classList.remove('dark-theme');
}

In this example, the theme automatically switches to dark mode during the evening and night hours (from 6 PM to 6 AM) and reverts to the light theme during the day.

2. Dynamic Theme Generation

Dynamic theme generation involves creating themes on the fly based on user input or external data sources. This can be useful for applications that require highly personalized experiences, such as design tools or customizable dashboards.

Example. Generating Themes Based on User Input

function applyCustomTheme(primaryColor, secondaryColor) {
  document.documentElement.style.setProperty('--primary-color', primaryColor);
  document.documentElement.style.setProperty('--secondary-color', secondaryColor);
}
document.getElementById('apply-theme').addEventListener('click', function() {
  const primaryColor = document.getElementById('primary-color-input').value;
  const secondaryColor = document.getElementById('secondary-color-input').value;
  applyCustomTheme(primaryColor, secondaryColor);
});

Here, users can input their preferred primary and secondary colors, and the theme is generated dynamically based on their selections.

3. Storing User Preferences

To enhance the user experience, it's essential to persist user preferences across sessions. This can be achieved using localStorage in the browser, ensuring that the selected theme is remembered even after the user leaves and returns to the site.

Example. Persisting Theme Preference

document.addEventListener('DOMContentLoaded', function() {
  const savedTheme = localStorage.getItem('theme');

  if (savedTheme) {
    document.body.classList.add(savedTheme);
  }

  document.getElementById('theme-switcher').addEventListener('click', function () {
    document.body.classList.toggle('dark-theme');
    const currentTheme = document.body.classList.contains('dark-theme') ? 'dark-theme' : 'light-theme';
    localStorage.setItem('theme', currentTheme);
  });
});

In this example, the user's theme choice is saved to localStorage, and it is automatically applied when the page loads.

4. Theming Based on System Preferences

Modern browsers support theming based on system-level preferences, such as dark mode. This allows web applications to adapt automatically to the user's operating system settings.

Example. Adapting to System Preferences

@media (prefers-color-scheme: dark) {
  :root {
    --background-color: #333333;
    --text-color: #ffffff;
    --button-background: #ff5722;
    --button-text-color: #000000;
  }
}

With this media query, the theme automatically switches to dark mode if the user's system is set to dark mode.

Final Example: Comprehensive Theming Solution

Combining the concepts we've covered, here's a complete solution that includes light and dark themes, dynamic theme switching, user preference storage, and adaptation to system preferences.

HTML(index.html)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Advanced CSS Theming</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <h1>Advanced CSS Theming Example</h1>
  <p>Explore advanced theming with CSS variables and JavaScript.</p>
  <button id="theme-switcher">Switch Theme</button>
  <script src="script.js"></script>
</body>
</html>

CSS (styles.css)

:root {
  --background-color: #ffffff;
  --text-color: #000000;
  --button-background: #007bff;
  --button-text-color: #ffffff;
}

body {
  background-color: var(--background-color);
  color: var(--text-color);
  font-family: Arial, sans-serif;
  padding: 20px;
}

button {
  background-color: var(--button-background);
  color: var(--button-text-color);
  padding: 10px 20px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

.dark-theme {
  --background-color: #333333;
  --text-color: #ffffff;
  --button-background: #ff5722;
  --button-text-color: #000000;
}

@media (prefers-color-scheme: dark) {
  :root {
    --background-color: #333333;
    --text-color: #ffffff;
    --button-background: #ff5722;
    --button-text-color: #000000;
  }
}

JavaScript (script.js)

document.addEventListener('DOMContentLoaded', function() {
  const savedTheme = localStorage.getItem('theme');

  if (savedTheme) {
    document.body.classList.add(savedTheme);
  }

  document.getElementById('theme-switcher').addEventListener('click', function() {
    document.body.classList.toggle('dark-theme');
    const currentTheme = document.body.classList.contains('dark-theme') ? 'dark-theme' : 'light-theme';
    localStorage.setItem('theme', currentTheme);
  });

  const hour = new Date().getHours();
  if (hour >= 18 || hour < 6) {
    document.body.classList.add('dark-theme');
  } else {
    document.body.classList.remove('dark-theme');
  }
});

In this final JavaScript code, we're combining multiple advanced theming concepts.

  • Loading a Saved Theme: The user's previously selected theme is loaded from localStorage when the page is loaded.
  • Theme Switching: Users can switch between themes using a button, and their choice is saved for future visits.
  • Time-Based Theming: The theme automatically switches to dark mode during night hours (6 PM to 6 AM).
  • System Preference Matching: The theme respects the user's system color scheme preference, automatically applying a dark theme if the system is set to dark mode.

Output

Output

Summary

CSS theming with variables and JavaScript opens up a world of possibilities for creating responsive, personalized, and context-aware web applications. By leveraging basic and advanced theming techniques, you can build interfaces that not only look great but also adapt seamlessly to user preferences and environments.

This article covered the foundational aspects of CSS theming and introduced advanced concepts such as dynamic theme generation, context-aware theming, and persistence of user preferences. Whether you're building a simple website or a complex web application, these techniques will help you deliver a polished and user-friendly experience.

If you find this article valuable, please consider liking it and sharing your thoughts in the comments.

Thank you, and happy coding.