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 has more detail. A number indicates that browser supports the feature at that version and up.
Desktop
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
49 | 31 | No | 16 | 10 |
Mobile / Tablet
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
126 | 127 | 126 | 10.0-10.2 |
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.
Great post. Custom properties do indeed serve well as an API for JS manipulation.
Something related and of note regarding custom properties. Declaring a class name like this
.--modifier
will break your CSS in Safari Webkit because it treats it as a custom property rather than a class name. I was using this naming convention to denote heavily used global modifiers to avoid duplicating code in my BEM components, only to find to my dismay that once Safari implemented the official version of custom properties, so much of my CSS was broken in Safari/iOS Webview…I don’t know why this would get parsed as a custom property when a dot clearly denotes a class name, always has in CSS and therefor should take precedent over customer properties. I hope Apple fixes in future releases, though now that it has been done, there will always be the possibility of this version of Safari/Webkit with the bug in it hanging around.
(check this pen out in safari to see what I mean) http://codepen.io/ericwshea/pen/ALwVPW
Safari is following the CSS specification. I’m surprised that other browsers are allowing that:
Source: https://www.w3.org/TR/CSS2/syndata.html#value-def-identifier
I highly recommend you don’t use
.--classname
in case this convention be used in future to something else, like variable class names.Thanks for the info! facepalms I should have checked the docs.
Is there a link to that article about conditionals? I suppose I could just google it.
The article was updated: http://kizu.ru/en/fun/conditions-for-css-variables/
Thanks for the comparison, Chris! The benefits of css variables are pretty exciting.
You forgot a link to Roman Komarov’s article, here it is: http://kizu.ru/en/fun/conditions-for-css-variables/
Mm no ie/edge support: not usable in production environment.
Please to make your blog’s clearer. Can you state caniuse stats ast the beginning of your blog posts? (Maybe Iframe so caniuse automatically updates).
The post is titled “What is the difference between CSS variables and preprocessor variables?” not “Are CSS variables ready for you to use use in production?”. So, you know, for clarity, I open with that, THEN get into browser support.
I have caniuse data right in the blog post, and it’s written in such a way that it pulls fresh data directly from there and caches it (few days at most).
Woho! Nice article Chris! Theme switcher demo using CSS Variables available on my article that I published the other day. Another use case :)
https://pawelgrzybek.com/css-custom-properties-explained/
I wrote about CSS variables last year (in French). I gave two examples of how great CSS variables can be to create simple CSS code.
The first example is to theme blocks of different colors. Same presentation, but with a main different color every time. You can see a demo without CSS variables and one with CSS variables. Compare the two CSS files (with and without variables) to see how clearner the code is with CSS variables.
The second example is to create generic sprite animations. You can create an animation with a variable value.
And then set the variable value and call this animation.
Compare a live demo of this technique without CSS variables and with CSS variables.
I think you can’t use css variables in media queries too.
Thanks for this.
Is this just me, but for me using Sass is great because of the modularity of it. In what workflow or project would people gain more efficiency using these css variables if they just simply not as consistent with their CSS?
For me, Sass is a huge benefit in that respect when working on larger projects.
It looks nice, but it is a no go for production without IE support unfortunately :/
Are there any PostCSS plugins like autoprefixer that can parse the custom properties for IE but allow them for Chrome and Firefox?
Thanks for the article. Can this be used in winLess framework?
Great CSS tricks with a lot of material.
very impressive and informative