DEV Community

Cover image for Easy Website Themes with CSS Custom Properties
Nathan Minchow
Nathan Minchow

Posted on

Easy Website Themes with CSS Custom Properties

With the advent of dark mode, website theme customization is becoming an expectation instead of a feature.

Plenty of websites go a step further and allow their users to select from multiple themes (like dev.to):

Dev.to theme selection page

Full website theme customization may be overkill for some sites, but it's still something we ought to keep in mind when designing and developing for the web. Thankfully, modern CSS includes features that make implementing website customization easy. Most of it boils down to CSS Custom Properties.

CSS Properties: A Quick Overview

CSS Custom Properties allow us to define reusable variables in CSS without a preprocessor. They aren't exactly new; most modern browsers have supported them since 2016. And since they are variables, we can update them dynamically.

Custom Properties can be defined on any element by prefixing the property name with --. If we wanted to create reusable properties on the root element, we could define them like so (these examples are taken from MDN):

:root {
  --first-color: #488cff;
  --second-color: #ffff8c;
}
Enter fullscreen mode Exit fullscreen mode

We can access these properties in child elements via the var() keyword:

#firstParagraph {
  background-color: var(--first-color);
  color: var(--second-color);
}

#secondParagraph {
  background-color: var(--second-color);
  color: var(--first-color);
}

#container {
  --first-color: #48ff32;
}
Enter fullscreen mode Exit fullscreen mode

To see this in action, I've defined three different colors in the example below. When the checkbox is toggled, the CSS properties update and colors change wherever they are referenced:

Custom Properties can do a lot of useful things. I recommend reading this excellent article by Michael Riethmuller for a more in-depth look at CSS Custom Properties, how to use them, and how they differ from preprocessor variables.

Theme Customization Use Cases

Since Custom Properties can be reused and updated dynamically, they are a good fit for theme customization. Let's look at how we can use Custom Properties to customize a site.

Implementing Dark Mode

The most straightforward way to add a dark mode to your site is via the prefers-color-scheme media query. This media query typically corresponds to the theme of the user's operating system.

So, if we have some scoped properties defined like so:

    main {
      --primary-color: cyan;
      --secondary-color: orange;
      --tertiary-color: yellow;
    }
Enter fullscreen mode Exit fullscreen mode

We can simply update their values in the media query:

    @media (prefers-color-scheme: dark) {
      main {
        --primary-color: gray;
        --secondary-color: darkgray;
        --tertiary-color: lightgray;
      }
    }
Enter fullscreen mode Exit fullscreen mode

And any elements using those properties will update dynamically when the user's theme changes. Here's how that might look in practice:

Operating system color selection reflected on a webpage

Custom Theme Selection

While prefers-color-scheme is a great starting point, sometimes we want to give the user the ability to select a theme at will.

If you've designed your site to take advantage of Custom Properties, we can accomplish this fairly easily. All we need to do is modify them, which we can do via CSS or Javascript.

Modify Custom Properties with CSS

Custom Properties, like any other CSS, can be updated as long as we have the proper selector.

If you examine the CSS from my earlier example, you'll notice that I've scoped my Custom Properties to the main element. When the checkbox is toggled, a selector updates the scoped properties inside it. This causes all elements within the main element to update with the new values:

While this method is pretty quick to implement, CSS selectors can be somewhat fickle (and dependent on our HTML). Furthermore, in most cases we'd want to save a user's theme choice. Javascript gives us more flexibility.

Modify Custom Properties with Javascript

We can use the setProperty() method to update our Custom Properties from Javascript.

If we have some Custom Properties scoped to a main element, we can query for it in our Javascript and call setProperty() to update its properties to new values:

It's common to see Custom Properties defined in the :root pseudo-class. In that case, the Custom Properties can be updated by calling setProperty on the root element:

While those examples only altered a few div elements, the same technique can be used to alter the theme for an entire site. To demonstrate this, I modified a template from HTML5UP to use Custom Properties for most backgrounds, text colors, and accents. Then, I added a toggle button that updates those properties with dark values instead:

Custom portfolio site with toggle theme button

The code for the theme switch is very similar to the CodePens above. I define a couple "Theme" objects in my Javascript:

const darkTheme = {
  "--accent-color": "#4acaa8",
  "--background-color": "#343737",
  "--active-scroll-background": "#343737",
  "--color-text": "white",
  "--sidebar-color": "#444c48"
};

const lightTheme = {
  "--accent-color": "#4bcdab",
  "--background-color": "#f0ffff",
  "--active-scroll-background": "#f0ffff",
  "--color-text": "#777",
  "--sidebar-color": "#4bcdab"
};
Enter fullscreen mode Exit fullscreen mode

Then, when the toggle button is pressed, I update the Custom Properties I've defined on the root element with properties from a given "Theme":

function applyTheme(theme) {
  let root = document.documentElement;
  root.style.setProperty("--accent-color", theme["--accent-color"]);
  root.style.setProperty("--background-color", theme["--background-color"]);
  root.style.setProperty(
    "--active-scroll-background",
    theme["--active-scroll-background"]
  );
  root.style.setProperty("--color-text", theme["--color-text"]);
  root.style.setProperty("--sidebar-color", theme["--sidebar-color"]);
}
Enter fullscreen mode Exit fullscreen mode

Feel free to take a look at the preview here, with the source code available here.

By implementing theme customization like this, adding a new theme to the site is as simple as creating a new theme object. We could save a user's preference via local storage or a database depending on what tools we have available.

Custom Color Selection

Some websites and apps allow users to create and modify themes directly. Once again, we can use setProperty() to update a Custom Property with any value, including ones exposed for input.

In the Codepen below, the colors of the first square and the borders of all squares are exposed as input elements. When the form is submitted, these values are updated and are reflected in the result:

Naturally, we could extend this to expose the various properties used on an entire website or application. Those preferences could then be exported or saved to remember a user's choice (or to allow users to share themes).

Conclusion

Custom Properties allow us to make sweeping changes to our website without much work. This makes them a great tool for implementing theme customization, whether for automatically detecting a user's theme preference with prefers-color-scheme or allowing them to pick (and potentially modify) their own themes.

Oldest comments (4)

Collapse
 
muhammedmoussa profile image
Moussa

CSS is awesome

Collapse
 
adam_cyclones profile image
Adam Crockett 🌀 • Edited

I had made a post about this already but you might achieve more flexible themes using hsla() and CSS variables, given that you can then tint or hue brighten and darken in a more intuitive way.

Collapse
 
adam_cyclones profile image
Adam Crockett 🌀

I love!! That slide to subscribe control that's actually thought provoking and requires some effort.

Collapse
 
shaonkabir8 profile image
Shaon Kabir

CSS is awesome. Last couple of days, I'm learning the coolest staff like animation using css. I've been using these for at least 3 years, but unfortunately don't try to learn what's happening behind the scene.

Thanks for a great share 😍😍