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.

The most important and interesting stories in tech. Straight to your inbox, daily. Get Versioning.
Login or Create Account to Comment
Login Create Account