DEV Community


Theming with CSS Custom Properties

TJ Fogarty
Developer from Limerick, Ireland.
Originally published at ・4 min read

I remember scoffing at the thoughts of variables in CSS. "Haha. Oh internet, we have Sass!" Alas, I was a fool. Now it's getting to the stage where I might say "Haha. Oh internet, we have CSS!" when faced with the option of using a pre-processor. There are still a few things I use pre-processors or post-processors for such as nesting and splitting files, but beyond that, I'd be just as happy using vanilla CSS.

I'm going to be showing the code used on this site at the moment, one difference being the name of some of the custom properties, but beyond that, it's the same. As I'm using Cr so I'll be explaining this with the Twig templating language, but the idea can be applied anywhere from server-side rendering to JavaScript in the client.


To create a custom property, we can use the :root selector and pass it properties that begin with --. This tells the browser that they're custom properties and the values can be whatever we want. In this case, we're using colours.

We're going to declare two colours; primary and secondary. The c prefix here is more of a visual cue to let us know these are colours should we need reminding:

:root {
  --c-primary: #7B296E;
  --c-secondary: #AFA73A;

I like to keep these at the top of a file where I make base styles such as on the body tag, but I might end up moving them to their own file in the future if it gets a bit unwieldy. In any case, they're set now and we can use them by calling the var() function and passing one of these property names:

a {
  color: var(--c-primary);

a:hover {
  color: var(--c-secondary);

By using these, the project becomes instantly themeable. I'm sure more consideration needs to be taken with bigger projects; maybe you don't want all of them to change, or have more control over what gets updated. In this case you can create new custom properties based on existing ones:

:root {
  --c-primary: #7B296E;
  --c-secondary: #AFA73A;
  --c-hero-title: var(--c-primary);

This way we can re-use existing values without duplication, but also target specific ones in certain cases if we need to.

The Templates

In my case, I'm selecting a random theme on every page load, so it makes more sense for me to have everything related to the theming in Twig files. Otherwise, if I scoped my themes in CSS to a class I'd have to update the Twig file and the CSS when a new one gets added or removed. If you're making a theme selector then it can be necessary if they change based on a class getting added to an element in the DOM. To each their own really, we just want to build cool stuff.

In our default Twig layout file, I've updated the opening html tag to this:

<html lang="en" {% include 'components/_style-switcher' %}>

This will inline some CSS to override the original variables. So what's in _style-switcher.twig?

{% set themes = [
    'primary': '#ff3b3f',
    'secondary': '#fcd200'
    'primary': '#7B296E',
    'secondary': '#AFA73A'
    'primary': '#02847F',
    'secondary': '#D99104'
    'primary': '#75003E',
    'secondary': '#8B8F00'
    'primary': '#8F0000',
    'secondary': '#005656'
] %}

{% set theme = random(themes) %}
style="--c-primary: {{ theme['primary'] }}; --c-secondary: {{ theme['secondary'] }};"

It's an array of objects, each one containing a primary and secondary key. {% set theme = random(themes) %} will select a random item from the array which we can then use to set the value of the variable. In this approach it won't set a unique one every single time, such is the nature of random I guess. So you could refresh the page a few times before a new one is chosen.

One approach to avoid this might be to set a cookie with the name of the chosen theme. On the next page load, you could check if the new randomly chosen one is the same as the one in the cookie. If so, try again, and repeat until you have a new one, and set that in the cookie.

If you're using JavaScript, you can use the following to update custom properties:

document.querySelector('html').style.setProperty('--c-primary', 'purple');


There's a range of ways you can approach this, and the easiest in some cases might be to do this with JavaScript if you don't have access to the server code side of things. Custom Properties absolutely reveals a more dynamic web, and you can do a whole lot more than theme with them. I usually pick a new feature such as this and try to apply it to my own work, be it existing or new, to have a play with it. And what's code if it's not playful at times?

This was originally posted on my blog.

Discussion (0)

Forem Open with the Forem app