The difference between SASS/LESS variables and CSS custom properties

and the advantages of using CSS custom properties (a.k.a. CSS variables)

Sandra
Netcetera Tech Blog

--

I started working as a front-end developer back in the time when people first encountered CSS as a way to customize the design of their accounts on Hi5 and MySpace (can’t believe these still exist).

No matter how much I find CSS to be a programming language, at the beginning it lacked the basic possibilities of using variables and functions/mixins, which we got by introducing pre-processors LESS and SASS/SCSS and suddenly our work was much easier — instead of using find/replace to change a hex color or a spacing value all over a CSS file (or multiple files), we could suddenly use a variable to change it only in one place in one file and it worked like a charm. Yey!

So, what’s the difference between those variables and the new CSS specification — Custom properties (a.k.a. CSS variables) and what is the advantage of using them?

The short answer is — because they are dynamic! The long answer follows :)

Photo by Pankaj Patel on Unsplash

Declaration

In LESS and SCSS you define a variable by using @variable-nameor $variable-name and assign them values just a regular CSS rule. Then you assign that variable to the CSS property (like color, padding, margin, etc):

Declaring a LESS and SCSS variable

In CSS custom properties, you use double dash --variable-name, but you have to define the scope of the variable, similar to doing so in JavaScript — by default if we want a global variable, we need to use the :root selector. Then, we assign the value to a CSS property by using var(--variable-name) — a function that uses the variable value — it’s a bit of a hassle to write it all the time but you get used to it.

Declaring a CSS custom property

The advantage of using var() is that it can accept fallback values if CSS custom properties are not supported by using another value as a function argument: (--variable-name, fallbackValue).

Using CSS custom property with a fallback value

Aaand you can nest these like var(--variable-name, var(--another-variable-name, fallbackValue) if you really want to make sure you have defined a value :)

You can declare all sorts of values like numbers, numbers with unit (px, s, rem, vh), decimal numbers, hex, rgb(), text strings, calc() operations, and even a simple javascript code that you can actually use within a javascript code. The only thing to watch out is to add the unit in the variable cause you cannot append it later on (but you can, for example, multiply it by *1px ;))

Static vs. dynamic

I will take an example with a SCSS variable and display how it compiles, on the left side you can see the SCSS code and on the right side you can see the compiled CSS that is actually sent to the browser — displayed exactly how you would see it in the browser dev tools.

How SCSS compiles

If we override the value in the cascade, then we get two values in the compiled CSS file that override each other.

How SCSS compiles with overriddes

The main downsides of the SCSS variables is that:

  • they are static — you cannot change them dynamically.
  • they are not aware of the DOM’s structure.
  • they cannot be read or changed from JavaScript.

And CSS custom properties cover those downsides :) They don’t compile like the pre-processor’s variables, so let’s see how they translate to the browser:

CSS custom properties translated in the browser dev tools

The basic difference with the static variable example is that we don’t really override the value of the variable *and* the property all over again (both the variable and its usage), but we override the value of the custom property only, in this case in the same scope :root.

If we override the value in a different scope, the one that is more specific will take over the value of the custom variable, just like the cascading overrides work (and similar to the javascript variables scoping).

Overriding a CSS custom property in a specific scope

In order not to override values when we don’t want to, for a more specific scope (in different context) we have to override the rule explicitly:

Overriding a CSS custom property and the CSS rule in a specific scope

In this example, the span always gets the color from it’s parent, until we explicitly define/override the color for it — the usual behavior of the cascade. If we just override the value of the custom property inside the span, we cannot actually change the color value for it’s parent — hence we have to override it specifically for it’s scope.

In this same case, if we want to remove the inheritance of the value of the parent’s color, then we need to set the value of the CSS custom properties to initial in order not to be inherited. And we actually want to inherit it in a specific scope, we override it with inherit in the scope where we want to.

Removing and applying inheritance of CSS custom properties

One of my favorite things of the CSS custom properties is that saves *a ton* of lines of code in overrides in specific contexts, like using @mediaqueries for targeting different screen sizes. In the following example we can see a CSS custom property applied to the :root scope which is overridden only for smaller screen sizes:

Overriding values in specific media contexts (different screen sizes)

Note that both of the lines are not displayed in the browser dev tools at the same time and we’re not actually overriding the rule, but using different value for that rule for a different screen size.

The most important lesson to remember is:

When you want to override a CSS rule that uses custom properties, just change the custom property’s value

Usage in JavaScript

I won’t get too much into details, but one of the most interesting feature of the CSS custom variables is the possibility to use it within JavaScript code and manipulate it on the face of the place — the DOM.

You can pass it on as an argument, set dynamic styles, get the current value, st another value or alter it somehow.

Usage of CSS custom properties with JavaScript code

One of the best usages I’ve seen is in Confrere’s presentation about their custom color-scheme product, where they used them along with js code to dynamically generate color palettes based on one color in order to achieve color accessibility.

Browser support

These past few years have been the most exciting in the CSS world since styling for the web exists with the introduction of CSS Custom Properties, Flexbox and Grid!

All modern browsers (including Edge, which is usually late) support CSS custom properties since April 2017. As a comparison — if you can use CSS Grid, you can actually use CSS Custom Properties without guilt and I’ll show a very neat example of their combination in a future post.

Browser support for Firefox, Chrome, Edge and Safari — all of them supported since April 2017

Feature queries

If you still need to support older browsers, here come the feature queries! Just as the @media queries target different screens, the feature query can check if some CSS property is supported.

You can actually use an old-school CSS and enhance it in a feature query block for modern browsers and it will be ignored in the older ones.

Using @supports to target browsers that support a certain CSS feature (in this case CSS custom properties)

Demo and links

The takeaway is that you can actually use *awesome* CSS even today! Even if you have to have older browser fallbacks — but it’s definitely worth checking if you actually have users that use older browsers at all — you might be surprised! :)

I needed to write this introduction in order to have a follow-up with a specific example that will follow as a post, which will probably be more interesting to read.

All of the code snippets can be found in this presentation which was held on the Front-end Meetup in Skopje https://slides.com/alexhris/css-custom-properties

Or you can play around with the following demo (including some js code):

Head out to CSS Tricks for an extensive overview and even more links: https://css-tricks.com/difference-between-types-of-css-variables/

Thanks for reading!

--

--