What you might not know about CSS variables

Everyone is talking about CSS variables! I've just spent some time getting my head around them and I can't thank Lea Verou enough for her fantastic talk on the topic, which is included at the end of this post. I'm excited to see what happens next in a world with native CSS variables.

CSS variables or "Custom Properties" (as per their correct name) allow you to use variables directly in CSS, with no compiling. They are accessed via the var() function, like this: var(--brand-color);. They are set using custom property  notation like this: --brand-colour: rebeccapurple; Here’s how it might look in a project:


:root {
  --brand-colour: rebeccapurple;
}
.header {
  color: var(--brand-colour);
}
.footer {
  background-color: var(--brand-colour);
}

Here's the same with a preprocessor. It's very similar.


$brand-colour: rebeccapurple;
.header {
  color: $brand-colour;
}
.footer {
  background-color: $brand-colour;
}

Why bother with CSS variables?

Why don't we declare CSS variables with a $? There are many differences between native CSS variables and preprocessor variables. CSS variables are properties which we define, hence the name custom properties. They look different to preprocessor variables (deliberately) so we can use both on the same stylesheet. So, what's the difference and why would you choose to use custom properties over preprocessor variables? Here are some reasons why you might:

Inheritance and cascade

When custom properties are applied to a DOM element, the value is passed onto it’s children. You can set a custom property inside any seclector to override its value.

In this example we can see the following:

The first box inherits its --main-colour value from the body element.

The second box has --main-colour and --border values applied directly to them, therefore overriding the previous value.

The final box has its --border value applied directly to it and it inherits the colour from the body. However, this time the inner div also inherits the border value.

See the Pen CSS variables inheritance by Lottejackson (@lottejackson) on CodePen.

The final box is interesting. The child div inherits the border from its parent as you'd expect, so why don't the child divs in the other boxes inherit their parents' borders too? This is because I controlled inheritance using the initial keyword (as seen in the example above):


* {
  --border: initial;
}

This effectively turns off inheritance for that custom property. Then it can be reinstated using the inherit keyword like this (also in the example above):


.item-3 {
  --border: inherit;
}

Custom properties take fallbacks

Custom properties take two arguments. You can use the second argument to provide a fallback like this: background: var(--alert-colour, red); So in here, red will be used if --alert-colour has not been set.

To provide a fallback for browsers which don't support custom properties we still need to use two declarations. The above example won't provide a fallback to IE, for example. So we should do this:


background: red;
background: var(--alert-colour, red);

Or perhaps you'll find feature queries useful:


@supports (--custom: properties) {
   /*code to run when custom properties are supported*/
}

They are editable in Javascript

Here's a simple drag and drop example where the value for --main-width is set in Javascript.

See the Pen Side drag & drop drawer JS & CSS Variables by Lottejackson (@lottejackson) on CodePen.

We don't need to write CSS styles in Javascript. We can just update variable values. To get a custom property from an inline style you can use: element.style.getPropertyValue('--variableName'). To get the custom property regardless of where it's set use this: getComputedStyle(element).getPropertyValue('--variableName'). Or to set it use this: element.style.setProperty('--variableName', mouseX);

CSS variables work in inline styles

The above Javascript example also illustrates that CSS variables work in inline styles like this: <div style='--main-width:100px'></div>

Some things you might not know about custom properties

  • They can't contain a single space as a value
  • Unlike any other CSS property, custom properties are case sensitive
  • You cannot use custom properties in property names or selectors
  • You can’t use them in a query part of a media query

It's also worth remembering the first ever CSS variable, currentColor. Browsers have supported currentColor for a while (including IE), so if that method gets the results you’re looking for, it might be better to use currentColor instead.

Use cases

I've seen quite a few examples of custom properties in action including theming, grids, generating content in CSS. All of which reduce code and make CSS easier to read and manage. I like that custom properties help keep styles out of Javascript. This might help organisations where CSS and JS are written by different teams.

I highly recommend reading and watching the following: