Variables are one of the major reasons CSS preprocessors exist at all. The ability to set a variable for something like a color, use that variable throughout the CSS you write, and know that it will be consistent, DRY, and easy to change is useful. You can use native CSS variables ("CSS Custom Properties") for the same reasons. But there are also some important differences that should be made clear.

A simple example of preprocessor variable usage is like this:

$brandColor: #F06D06;

.main-header {
  color: $brandColor;
}
.main-footer {
  background-color: $brandColor;
}

That was the SCSS variant of Sass, but all CSS preprocessors offer the concept of variables: Stylus, Less, PostCSS, etc.

The above code would do nothing in a browser. The browser wouldn't understand the declarations and toss them out. Preprocessors need to compile into CSS to be used. This code would compile to:

.main-header {
  color: #F06D06;
}
.main-footer {
  background-color: #F06D06;
}

This is now valid CSS. The variable was part of the preprocessor language, not CSS itself. Once the code compiles, the variables are gone.

More recently, native CSS has started supporting CSS variables, or "CSS Custom Properties". It allows you to work with variables directly in CSS. There is no compiling.

A simple example of CSS custom property usage is like this:

:root {
  --main-color: #F06D06;
}

.main-header {
  color: var(--main-color);
}
.main-footer {
  background-color: var(--main-color);
}

These two demos achieve the exact same thing. We were able to define a color once and use it twice.

So then... why use one over another?

Why would you use native CSS custom properties?

  • You can use them without the need of a preprocessor.
  • They cascade. You can set a variable inside any selector to set or override its current value.
  • When their values change (e.g. media query or other state), the browser repaints as needed.
  • You can access and manipulate them in JavaScript.

Regarding cascade, here's a simple example of that:

:root {
  --color: red;
}
body {
  --color: orange;
}
h2 {
  color: var(--color);
}

Any <h2> will be orange, because any <h2> will be a child of <body>, which has a higher applicable specificity.

You could even re-set variables within media queries and have those new values cascade through everywhere using them, something that just isn't possible with preprocessor variables.

Check out this example where a media query changes the variables which are used to set up a very simple grid:

Rob Dodson advocates for CSS Custom Properties in CSS Variables: Why Should You Care?

The variables that [preprocessors] use suffer from a major drawback, which is that they’re static and can’t be changed at runtime. Adding the ability to change variables at runtime not only opens the door to things like dynamic application theming, but also has major ramifications for responsive design and the potential to polyfill future CSS features.

He includes a demo where JavaScript changes styles. It doesn't change the styles on elements directly, it's just resetting some CSS variables on-the-fly:

Wes Bos has a demo of this in action as well:

See the Pen Update CSS Variables with JS by Wes Bos (@wesbos) on CodePen.

Note there is a bunch of stuff about CSS custom properties I'm leaving out here. You can set fallbacks. You can use calc() with them. There are a bunch of cool tricks you can do with them. See the homework section below!

Why use preprocessor variables?

  • The big one: There are no inherit browser support considerations. They compile down into normal CSS.
  • Little stuff: Like you can strip units from a value if you had to.

You could use them together

There are pretty compelling reasons to use both. You could absolutely have a CSS preprocessor output CSS custom properties. Ivan Ivanov created a demo that allows you to write using the syntax of CSS custom properties, and through Sass, output code that has fallbacks:

See the Pen Use CSS4 variables right now by $i.van(ov) (@vank0) on CodePen.

I tend to think that once we can use CSS custom properties without worrying about browser support, that we'd just use them to do all our variable handling. We might still use preprocessors for other conveniences, but the variable handling in native CSS seems so good it's probably worth just going all-in on that.

Browser support of CSS Custom Properties

This browser support data is from Caniuse, which also reports this feature is in W3C Candidate Recommendation status.

Desktop

Google ChromeMozilla FirefoxInternet ExplorerOperaApple Safari
4931No369.1

Mobile / Tablet

iOS SafariAndroidOpera MobileAndroid ChromeAndroid Firefox
9.353375550

Homework time: level up!

1) Watch Lea Verou's CSS Variables: var(--subtitle);

She covers plenty of practical applications, as well as some trickery like taking control of when variables cascade and some gotchas.

2) Watch David Khourshid's Reactive Animations with CSS

David shares the idea that connecting DOM events with CSS variables can do some really awesome UI stuff with not much code. Check out his slides (starting from #26) that show off how awesome this is.

3) Read Harry Roberts Pragmatic, Practical, and Progressive Theming with Custom Properties

His article explains how user theming of sites gets a heck of a lot easier with CSS variables.

4) Read Roman Komarov's Conditions for CSS Variables

Despite it being talked about every so often, there are no logical gates in CSS (e.g. @if (true) { }). We fake it sometimes with things like :checked, but that's dependent on the DOM. Roman shows off a trick where you can use 0 or 1 on a variable and then use that with calc() to simulate boolean logic.