Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSS variables with fallback for older browsers

TL;DR: How can you use SCSS to have CSS variables with a fallback for older browsers.

I'm trying to make sense of this article. In my opinion, you have to already be an advanced SASS user to understand it, which I'm not. To make matters worse, it's the only article I found on the subject.

Here is what I'm trying to achieve:

My scss should be along the lines of :

body {
  @include v(background-color, primary)
}

then the processed CSS should be

body{
   background: yellow; /* Yellow being defined above as the primary color */
   background: var(--color-primary);
}

By playing around a bit, I can already get the value of the CSS variable like so:

$colors: (
  primary: yellow,
);

:root {
  @each $name, $color in $colors {
    --color-#{$name}: $color;
  }
}

@mixin background-color($color_) {
  background: var(--color-#{$color_});
}

To use it:

body{
  @include background-color(primary);
}

Which will result in this:

body {
    background: var(--color-primary);
    /* But the fallback is missing :(, I tried  things with the map-get but it's really eluding me... */
}
like image 712
Ced Avatar asked May 30 '17 21:05

Ced


People also ask

Are CSS variables supported on all browsers?

CSS Variables (Custom Properties) on Chrome is fully supported on 49-106, partially supported on None of the versions, and not supported on 4-48 Chrome versions. CSS Variables (Custom Properties) on Safari is fully supported on 10-16, partially supported on 9.1-9.1, and not supported on 3.2-9 Safari versions.

Can I use CSS variables in SCSS?

CSS variables are included in the CSS output. CSS variables can have different values for different elements, but Sass variables only have one value at a time. Sass variables are imperative, which means if you use a variable and then change its value, the earlier use will stay the same.

Should I use CSS variables or SCSS?

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.

What is a fallback CSS?

The fallback descriptor can be used to specify a counter style to fall back to if the current counter style cannot create a marker representation for a particular counter value.


2 Answers

If you're using Sass, you can automate fallbacks through a Sass mixin. Create a map of your CSS variable names and their values, and then you can look up those values in a mixin that outputs the fallback style and the preferred one

$vars: (
  primary: yellow,
);

:root {
  --primary: map-get($vars, primary);
}

@mixin var($property, $varName) {
  #{$property}: map-get($vars, $varName);
  #{$property}: var(--#{$varName});
}

The above mixin is used like so:

body {
  @include var(background-color, primary);
}

and outputs the following CSS:

:root {
  --primary: yellow;
}

body {
  background-color: yellow;
  background-color: var(--primary);
}

Et voilà :)

like image 64
rdhainaut Avatar answered Sep 21 '22 03:09

rdhainaut


Update: Postcss Custom properties can do fallback and is way easier than the below code

step 1: declare scss variables

So first of all we want to put some variables in a $map, I'll go with color variables:

$colors: (
  primary: #FFBB00,
  secondary: #0969A2
);

step 2: automate css 4 var generation

// ripped CSS4 vars out of color map
:root {
  // each item in color map
  @each $key, $value in $colors {
    --colors-#{$key}: $value;
  }
}

What happens in root is : for each key and value in the colors map, we print the followng :

--colors-#{$key}: $value;

Which corresponds to css variable declarations. I believe the weird bit with #{} around the key is to not have spaces around the value. Thus the result is:

--colors-primary: #FFBB00,
--colors-secondary: #0969A2

Note that the prefix (--colors-) is the same name as the scss color map above it. The why will become clear in last step.


step 3: Plenty of maps !

$props: (
  background-color: $colors
);

$map-maps: (
  background-color: colors
);

Here we add the map $props which maps a css property to the map containing the values. background-color will hold color, so the correct map is $colors.

map-maps is a copy of props where instead of the map we have the name of said map. (this is relative to the note in step 2).

Step 4 : let's make it work !

@mixin v($prop, $var) {
  // get the map from map name
  $map: map-get($props, $prop);
  // fallback value, grab the variable's value from the map
  $var-fall: map-get($map, $var);
  // our css4 variable output
  $var-output: var(--#{$map}-#{$var});    
  #{$prop}: $var-fall;
  // css4 variable output
  #{$prop}: $var-output;
}

body{
  @include v(background-color, primary);
}

I simplified the code in the article quite a bit, it still works, for this example at least, the code in the article takes more into account.

Anyhow, here is what happens.

First, we call the mixin with:

  @include v(background-color, primary);

Then upon entering,

 $map: map-get($props, $prop); // map-get($props, background-color)

we have a variable called $map to which we assign the value that is inside the $props map at the key background-color which happen to be the $colors map. It's a bit of a maze but it's not that complicated once you resolve it.

Then for the fallback:

 $var-fall: map-get($map, $var);

This simply gets the value of the map we just got (which is $colors) at the $var key (which happens to be primary). Thus the result is #FFBB00.

For the css var

  $map-name: map-get($map-maps, $prop);
  $var-output: var(--#{$map-name}-#{$var});

we recreate what we did to generate the var in the @each loop


Whole code would be :

$colors: (
  primary: #FFBB00,
  secondary: #0969A2
);

// ripped CSS4 vars out of color map
:root {
  // each item in color map
  @each $name, $color in $colors {
    --colors-#{$name}: $color;
  }
}



$props: (
  background-color: $colors,
  color:            $colors
);

$map-maps: (
  background-color: colors
);



@mixin v($prop, $var) {
  // get the map from map name
  $map: map-get($props, $prop);
  // fallback value, grab the variable's value from the map
  $var-fall: map-get($map, $var);
  // our css4 variable output

  $map-name: map-get($map-maps, $prop);
  $var-output: var(--#{$map-name}-#{$var});

  #{$prop}: $var-fall;
  // css4 variable output
  #{$prop}: $var-output;
}

body{
  @include v(background-color, primary);
}

Now this is a simplification of what is done in the article. You should check it out to have code a bit more robust.

like image 32
Ced Avatar answered Sep 20 '22 03:09

Ced