Variables for Both
Now that CSS Custom Properties, or CSS Variables, are becoming a solid standard, I’m using a method to map their values to CSS Variables whilst providing a value-as-is fallback using a straightforward syntax in a SCSS function and mixin.
Since writing this post, I have made a number of small tweaks to the original mixin and now function, and I’ve reflected those changes in the contents below.
One of my favourite parts of developing for the web is the ever-shifting landscape and the opportunity to work with new technologies. Sometimes they aren’t apt for use in production, but fortunately for us today, CSS Variables are pretty reliable to use on their own. However, if you’re in a position similar to mine and find yourself often needing to support old versions of Internet Explorer or making sure all of your browser support bases are covered, this SCSS setup should be useful for you.
I won’t pretend I’m writing the introduction to a family recipe for chocolate cake and bore you with twenty minutes of introduction; instead, let’s just jump into the technique, then I’ll break it down a little bit. But before I get too ahead of myself, please keep in mind there are some bits that require setting up!
For the purposes of this example, I’ll create a simple mapping for
z-index. Let’s start by setting up a variable for each level of
z-index that we want to use. This can help remove the mental overhead of setting values for this property, as it’s generally obvious what level an element should live at when they’re named in a way that makes sense to you (and your team). This part is optional.
And the next step is to create a Map of these values. You don’t actually need to use the variables for each value in the Map; you can instead put your typical CSS property values in their place—whole numbers in the case of
Now we need to prepare our CSS Variables. In order to make them available for the entire scope of our (S)CSS, we’ll assign them underneath
Additionally, because we’re using SCSS, and to keep our code DRY, we’ll utilise the SCSS Map that we set earlier and iterate over it to create a CSS Variable for each of our SCSS Variables and Map values.
Now we’ve established our SCSS Variables, a Map for them to live in and be iterated over, and the CSS Variable equivalents of those SCSS Variables. There is, however, one more thing we need to do before we can pull this all together inside our function and mixin.
We could hard-code some logic in order to designate that our numeric values should be used against the
z-index property, but I prefer things to be more robust than that. In this case, we’ll use two more SCSS Maps to hold some relational information about the properties and the possible values we want to be able to use inside them.
Before that, though, let’s expand our example a little bit and include two more CSS properties in this methodology:
border-width. This will help to illustrate the value in creating these relational Maps.
One important thing to note about this new code is that we’ve introduced a new set of variables,
measures, which are used as values for properties like
measures establish consistent spacing and sizing across your components. We will be using the --opacity-
type variables for
opacity but the --measure-
type variables can be used for a great number of properties.
This is where the penultimate step really shines. First we’ll establish which SCSS Map of Variables should be used for each
type by preparing $variable-map.
Secondly, we’ll create a Map that relates each CSS property to a set of values of a particular
Things are pretty straightforward for
opacity, but you can see that we’ve now assigned our
measure-type variables to a handful of properties—we can use any of our measures (small, medium, large) when assigning a value to
Lastly, we’ll check our passed-in value against a list of “generic” CSS property values, which includes: auto, inherit, initial, none, revert, unset, 0, and 1. If it does match one of those generic values, we’ll forego any processing and output a singular property-value pair.
Let’s tie it all together with this SCSS function and mixin.
You might have noticed that there are a number of parameters you can pass to the function and mixin: the property, a value, and whether or not to provide the fallback value (optional).
And if we want to do any kind of computation, modify the value, or combine values under a multi-value property (e.g.
border) in any way, we can use our function instead of the mixin:
And by modifying the third parameter,
$fallback, we can return the computed SCSS values instead:
Changing the default value of $fallback from
false on the mixin itself will have a knock-on effect across your codebase, and for every @include v(...);, you’ll be shaving off a line of code in your compiled CSS—not much, especially after compression, but it might add up if you are consistently using this technique across your codebase.
The benefits to using CSS Variables are enormous, and I’d strongly recommend using them as soon as you can across your projects. Others have better explained what those many benefits are, so I encourage you to read up on the subject. A function or mixin like the ones we’ve gone over in this article will help you both in transitioning towards using CSS Variables as well as, when the time comes for your project(s), ceasing to provide fallbacks for your CSS Variables.
This technique could also certainly use some extra eyes to tighten up the code and make it more approachable to a wider audience. Let me know if you have any suggestions or feedback—I’d love to make this technique even stronger, or maybe you have an even better solution!