By Daniel Imms

What CSS Variables Can Do That Preprocessors Can’t

By Daniel Imms

Mozilla Firefox recently implemented the highly anticipated CSS variables spec. Since then, some articles started appearing hinting that they are now largely obsolete with the advent and widespread use of preprocessors like Sass, Less, and Stylus. I would argue however that they provide something that preprocessors can’t, well at least can’t efficiently.

Preprocessor variables are static; compiling a Sass file with variables will always lead to the same, fixed CSS values in the output file. Native CSS variables however can change at runtime, allowing the implementation of things like theming to be done more elegantly. In addition to this, they’re also scoped, enabling a variable to be changed on only a subset of the DOM tree.

Let’s look at this in more detail.

Site Themes without CSS Variables

Let’s try to implement color themes for a site to illustrate the usefulness of CSS variables. Your task is to change various colors based on a class on the <body> element. In CSS without CSS variables you would do that something like:

.red-theme .primary-color {
  color: #801515;

.red-theme .primary-color-bg {
  background-color: #801515;

.red-theme .secondary-color {
  color: #D46A6A;

.red-theme .secondary-color-bg {
  background-color: #D46A6A;

.red-theme .tertiary-color {
  color: #AA3939;

.red-theme .tertiary-color-bg {
  background-color: #AA3939;

This results in 6 individual selectors in the resulting CSS – and that is just for one theme. In addition to that, you would need either the classes above peppered throughout the markup, or to add the explicit selectors into the theme styles CSS, both of which aren’t very maintainable or space-efficient solutions to the problem.

So this means, either:

<h1 class="tertiary-color">For Example</h1>
<button class="primary-color-bg">A button</button>
<a class="secondary-color">A link</a>


.red-theme .primary-color-bg,
.red-theme button {
  background-color: #801515;

.red-theme .secondary-color,
.red-theme a {
  color: #D46A6A;

.red-theme .tertiary-color,
.red-theme h1 {
  color: #AA3939;

Site Themes with CSS Variables

Implementing this using variables is far more efficient. In such a case, you would define the color variables on .red-theme and then use them directly on your other styles. This will keep the style information out of your markup, where it doesn’t belong, and with the CSS selectors, where it does.

.red-theme {
  --theme-primary: #801515;
  --theme-secondary: #D46A6A;
  --theme-tertiary: #AA3939;

button, a {
  background-color: var(--theme-primary);
  color: var(--theme-secondary);

h1 {
  color: var(--theme-tertiary);

This is a far more elegant solution to the problem. In addition to this, as mentioned earlier, they’re scoped, so nothing is stopping us from using a different theme on say the navigation bar.

<body class="red-theme">
  <nav class="blue-theme">
    <button>Blue button</button>
    <button>Red button</button>

Beyond Site Themes

There are a bunch of applications beyond themes where CSS variables can extend the capabilities of preprocessors. Another example is implementing a spacing system similar to what Gmail has – allowing the user to select whether they want the UI to be compact, cosy, or comfortable.

.compact {
  --option-padding: 0.1em 0.2em;

.cosy {
  --option-padding: 0.3em 0.6em;

.comfortable {
  --option-padding: 0.5em 1em;

.list-item {
  padding: var(--option-padding);

Final Thoughts

I’ve shown here how to apply the upcoming CSS variables feature in a way that is distinct from the capabilities of preprocessor variables. Remember though that CSS variables are currently only enabled in the latest Firefox and Firefox for Android, so this is just a taste of what’s to come.

  • Xam1311

    hi, with sass you can do the same with a sass control directive i’ll post you an example on sassmeister this morning

    • LouisLazaris

      That would be great to see, thanks!

    • When I say about efficiency, I’m talking about the size of your resulting CSS. Control directives are obviously just another way that write raw CSS. Unless there is another way I didn’t consider to do it with raw CSS?

  • Alex Walker

    Things might eventually change, but — for all their coolness — I feel like CSS variables are unworkable for the foreseeable future. If my CSS3 border-radius fails in IE8, big deal — I get square corners. If my CSS variable fails in IE8 I get NO color. That’s more than likely a complete and utter site-wide showstopper.

    I understand more and more browsers will support variables over time, but, as far as I can see, there’ll never be an elegant fallback for browsers that don’t. And to me that dooms the idea of CSS variables.

    • Olgierd Grzyb

      There is which polyfills CSS variables and some other modern stylesheets’ features. But I prefer LESS or Sass anyway ;)

      • Nice find. It says it’s a preprocessor and ‘like’ a polyfill though, so it will suffer from the same issues as the other preprocessors (be static after compilation).

    • I see your point, but there was a time when people felt just as hopeless about IE6 never going away. As more people move to the newer evergreen browsers I believe this will be less and less of a problem moving forward.

      I imagine some clever folks could also develop a polyfill for browsers that don’t support it. If all you need to worry about to support CSS variables widely is to include a script then their use would be much more appealing.

      • Alex Walker

        I thought about polyfills before I wrote the comment above — in fact I stopped and kicked off a discussion with some of the devs around me.

        Won’t any polyfill ( for example) just be Sass/Less by another name? You write a super-language that gets compiled to normal CSS. We *could* get JavaScript to polyfill old browsers at runtime (like Less did) , but that feels a bit brittle and puts more work on old browsers that are already struggling.

        Regardless, I thought it was a great article. These types of discussions are often as valuable as any article. Would love to read more.

        • A better option surely would be to compile the CSS variables as SASS does, but on the fly for browsers which don’t support them natively. That way, it’s only runtime changes which won’t work in older browsers.

          > Won’t any polyfill ( for example) just be Sass/Less by another name?

          To begin with, yes, but once the browsers support it natively, the extra layer of complexity won’t be needed. Really, browsers should just accept plain SASS and not reinvent the wheel.

  • Olgierd Grzyb

    Well, what’s the difference between this and writing:

    .compact { padding: .2em }
    .cosy { padding: .3em }
    .comfortable { padding: .5em }

    It’s even shorter, also LESS and Sass variables are scoped too (but it would be nice to have the feature in the language, of course!)

    • What does the markup look like for these styles? How do you change the padding level on multiple elements? It’s all about trade offs, doing it will way will mean you have more markup to send to the user and need to change multiple classes on multiple elements to change the padding. The variable approach only requires a single class change on the body element.

      While they are scoped in the preprocessor, they aren’t in the generated CSS. The benefit of this I mention is that you could have different themes on different portions of the pages, all of which can be changed with a single element class change.

      • Olgierd Grzyb

        Ok, now I see the point. The problem could be solved using something like this:

        @mixin size() {
        .compact .cosy .comfortable }

        .list-item {
        @include size;

        I consider the preference of solution (native or preprocessor) subjective. I personally prefer the preprocessor solution, as it seems to be more DRY.

        • DRY in Sass, but it repeats itself quite a lot in CSS which could be an issue depending on how many times the mixin is used.

          • Yeah, but there are CSS optimizers which compact such repeats into single selectors, as an example.

          • I reckon that a mixin might be repeating too much. That’s why there’s the “extend” functionality :) Simply extending .compact OR .cosy OR .comfortable might be a better solution than this…

            It’ll become this in the end:
            .cosy, .extended-div1, .extended-div2 { … }

          • fuddy duddy

            Are there plans to add real mixins to css natively? I’d get more use of those. Could even use mixins in place of variables.

  • Tim

    I guess I am in the minority here, but I like to have control over every single element in my CSS. I don’t really see a point to variables, especially when find and replace is so easy to do.

    • Alex

      You don’t loose control over your elements when you’re using variables. Don’t know about you, but when I’m making a design, I’m following a chosen color scheme, and I’m using the same colors over and over again.
      Why I have to put that #3A3A3A on 312 places, when I can just put a variable. I dont want to have 10 different grey colors, and I want to be able to change them fast and easy.

      Yes, Find&Replace works, but its so much more beautifull to have variables.less, with all the configurations there, and just tweak it all from one place.

      Furthermore, what about things like line-heights, font-sizes and etc. ? You cannot Find and replace all “16” with “18”, but you can put a variable.

      I believe you just didn’t had the chance to learn and use the true power of pre-processors

  • LouisLazaris

    Daniel Imms Not sure if you saw this yet, but Peter Gasston has posted this:

    Which references this article and describes another benefit to native variables.

  • I think once support is in all browsers, we can all consider using this. Me, I’d rather use a native feature as it should be faster, more flexible and no maintenance. SASS may actually be a hinderance here, it is a system that many use, and so they think this integration is not required. I dont think that is the case.

  • Murray Chapman

    Crap. Even more to learn.

  • fuddy duddy

    –theme-primary: #801515;

    Some people will continue using preprocessors for years to come simply because the — syntax is ugly. Why in the world did they choose that?

    @var, -var-foo, %foo%, !foo, $foo, var(foo), … ANYTHING else I’d prefer.

  • Like a few of you here, I’m not really a CSS variable fan. I think they break the backward-compatibility that is the cornerstone of the Web’s success. I wrote about it here:

    • Rein Petersen

      CSS variables don’t break backward-compatibility – they break forward-compatibility. Only browser vendors can solve that by supporting the specification. The “fathers of css” need to take their heads out of web 1.0 so we can move on with modern web development without pre-process garbage like less and sass

Get the latest in Front-end, once a week, for free.