Native CSS variables vs. SCSS variables

CSS variables are here to stay. The browser support arrived on Chrome already on March 2016 but they seem still not to be used that much. The reason behind this could be the dominance of CSS preprocessor languages like SCSS that have been having variables way longer and people still stick to ways provided by them. I use SCSS way of writing CSS myself and flexibly use both SCSS and CSS vars, in a sense the same way I use both flex and grid.

In this blog post we’ll focus on colors.

When to use CSS variables?

Whenever you can. The advantage of CSS variables is undisputed. They can be transformed and overridden whereas SCSS variables can not. CSS variables simplify creating color theme based sites like this right one right here. Rolle.design has a dark mode (there’s a toggle switch on top right corner on desktop or on the bottom of the site on mobile) that has been achieved by simply replacing colors with CSS variables.

I prefer using CSS variables always in pricinple but in some cases they can’t be utilized the best way possible. These situations are:

As writing this on April 2021 the browser support for CSS variables is 95.06% globally.

I used SCSS variables a lot in things like this in variables/_colors.scss:

$color-brand-grey: #ccc;
$color-grey: $color-brand-grey;
$color-grey-80: darken($color-brand-grey, 20%);

And in things like this:

.button {
  @include $button($color: $color-grey-80);
}

As it happens you can mix up both SCSS and CSS variables in the same environment like in this variables/components/_button.scss example:

// Colors should really be in their own file
// but let's have them here in this example
:root {
  --color-wow-css-color: #f02c93;
}

@mixin my-awesome-button($color: var(--color-wow-css-color)) {
  color: $color;
}

This will actually work fine. If you need you can even define colors like this in your variables/_colors:scss:

// CSS Variables for colors
:root {
  --color-east-bay: #545773;
}

// SCSS Variables for colors
$color-buttons: var(--color-east-bay);

But 👏. There’s always a but. If I change from SCSS variable to CSS variable here…

// Colors should really be in their own file
// but let's have them here in this example
:root {
  --color-wow-css-color: #f02c93;
}

.skip-link:focus {
  color: choose-contrast-color(var(--color-wow-css-color));
}

…my gulp styles task produces an error: “Argument $color must be a color”.

Same happens with some custom SCSS functions that rely on variables in SCSS format with the dollar character. for example darken() or lighten() that I use. They won’t translate CSS variables into colors.

For rgba() it gets a bit more complicated since the native rgba function in CSS accepts only RGB color format whereas SCSS version accepts SCSS vars, hex and rgb formats which it ten processes automatically to RGB format in outputted CSS. However, when there’s a CSS variable in place it links to the hex color which is as-in, plain text. For us to be able to use CSS vars we need to convert the hex provided by var to RGB version of the color first. For this purpose I have a helper function called hextorgb():

// Convert hex color to RGB color format
@function hextorgb($hex) {
  @return red($hex), green($hex), blue($hex);
}

After I have that helper imported, variables/_colors.scss file needs to be formatted like this:

// Colors that we need as SCSS variables
// for mixins and other SCSS-related functions like rgba
$color-black: #222;
$color-pattens-blue: #eaf1f8;
$color-valhalla: #2a2d3e;
$color-main: $color-valhalla;

// CSS variables
// Names: https://www.color-blindness.com/color-name-hue/
:root {
  // Colors
  --color-east-bay: #545773;
  --color-black: #{$color-black};
  --color-black-rgb: #{hextorgb($color-black)};
  --color-east-bay: #545773;
  --color-pattens-blue: #{$color-pattens-blue};
  --color-pattens-blue-rgb: #{hextorgb($color-pattens-blue)};
  --color-pattens-blue-20: darken($color-pattens-blue, 20%);
  --color-white: #fff;
  --color-white-smoke: #f5f5f5;
  --color-valhalla: #{$color-valhalla};

  // Brand colors
  --color-links: var(--color-valhalla);
  --color-main: var(--color-valhalla);
}

This way you only need to define colors once and the rest is automatic. We can then use the rgba like this:

kbd {
  box-shadow: 0 1px 0 rgba(var(--color-black-rgb), .2), 0 0 0 2px var(--color-white) inset;
}

And darkened colors like this:

.edit-link a:hover,
.edit-link a:focus {
  color: var(--color-pattens-blue-20);
}

Long live SCSS, long live CSS!

Comment on Twitter