Why you need to use CSS variables

Ben Schwarz explains what user-defined CSS variables will do for developers.

It strikes me that there are still CSS purists who don't believe that they need a CSS processor. Maybe you're one of them? Perhaps you've already pulled together a process that you're entirely happy with. Or perhaps you're on the other side of that coin: you use CSS preprocessors (like Sass or Stylus) and see no need for native/browser-processed CSS variables. In this article I hope to debunk the arguments for both parties.


If you're going through the process of theming an application, creating your own custom components, or even using other people's components, you're probably hoping to be able to customise these modules a bit without having to modify the original code.

Let's say that you're using a grid system from Bootstrap, but the designer has worked slightly different gutter widths into the grid. Right now, you have roughly three choices:

  1. Modify the Bootstrap CSS code (thus making future upgrades near impossible without significant analysis)
  2. Convince the designer to change the grid to match Bootstrap (or try to sneak it past them)
  3. Include all of the unprocessed Bootstrap Sass, incorporate it into your build system and add your own custom variables

The third choice, for many, is the path of least resistance – but along the way, your build system becomes bigger, slower and more dependent on the entire team understanding everything that's going on. (Hey, nothing is perfect, right?)

Throw away your build system

Now that we understand the problem a bit better, we can start to talk about where CSS variables will shine. Firstly, let's take a look at the CSS variable syntax:

:root {
  --gutter-width: 1rem;

Here I've defined a variable named gutter-width and set it to 1rem. You'll note that the selector that I've chosen to use is :root which basically equates to 'HTML' (or the root SVG node). I'm using it to showcase a very important aspect of CSS variables: the cascade. Yes, variables can cascade through the tree just like properties!

So when I select an element elsewhere in my stylesheet:

.my-module {
  margin-right: var(--gutter-width);

The gutter-width variable cascades down from the root element. Say I want to tweak the gutter in one particular area only? Easy!

.my-module {
  --gutter-width: .5rem;
  margin-right: var(--gutter-width);

Suddenly, you don't need a complex build system. Just sprinkle in a couple of simple variables and customise to your heart's content. Stringing together a series of changes is even easier when you realise that any browser that supports variables has had CSS calc for a long time already:

:root {
  --gutter-width: .5rem;
.col {
  --column-width: 4rem;
  width: calc(var(--column-width) - var(--gutter-width))

In the above example, we've calculated the width of the .col selector by subtracting column-width.

A shaky start

Like many new features of CSS, the path to being shipped to the browser and users is a long, drawn-out, convoluted process. CSS variables are no exception – the syntax has changed drastically a number of times already, and, at the time of writing, Google Chrome has entirely removed the feature until everything settles down.

Firefox 31 is currently the only browser that ships with native CSS variables. But it'll be fine. In this article, I'll show you how to get started today with CSS variables.

Post-processing our way out of the problem

With so many disclaimers about which browsers support CSS variables, you're probably thinking: "Great, here's something that I can't use for five years." While we can't exactly polyfill variables, we can at least use existing tooling to help pretend that variables are fully supported everywhere.

Rather than relying on using a preprocessor (and therefore the preprocessor's syntax for variables), we can try something new: a post-processor. A post-processor is different from a preprocessor only in name. Post-processors do not aim to extend CSS: in fact, the idea is that you code as if you were using recently released CSS standards (or edge, in the case of variables), and it'll fill in the blanks.

On myth and rework

Myth (by Ian Storm Taylor) is a post-processor that pulls together a series of rework-powered plugins. It provides calc, variables, colour mixing and CSS browser vendor prefixes (thanks to the incredible autoprefixer project).

:root {
    --column-width: 3rem;
    --column-gutter: .5rem;
.column {
    min-width: var(--column-width);

Suddenly, you can start to pretend that it's already 2019. There is no need to labour over what's supported in each browser: you just write the code once.

Getting started is easy too, as Myth is an NPM package. After you've installed, it's as easy as running the following on the command line:

myth futuristic.css clever.css

Now your clever.css file is ready for the browser.

A final word

Not only am I excited about the future of CSS but also especially excited about where CSS variables will take designers and developers – hopefully, toward a simpler, more understandable, lighter tool chain where sharing code is absolutely trivial!

Ben Schwarz is an independent web dude from Melbourne. He's a team member of CSSConfAU, JSConf and Bower. This is an extended version of an article from net magazine issue 259.