Josh Vickerson
Posted on August 26, 2023
Note: This post was originally published to my blog on October 13th, 2022.
I was inspired by a brief exchange of tweets today with Brandon McConnell about the idea of a "native function for fallback values" in CSS, who was commenting on a wide gamut color demo by @bramus which also demonstrated how custom properties can be used to provide fallback values. Both the idea of a "native function" and custom properties have interesting usecases for providing fallbacks, but it's worth noting that CSS has always offered a native means of providing a fallback value in the form of the cascade.
In case you need a refresher, here's an approachable explainer on the CSS cascade.
Native Fallbacks in CSS
Simply put, repeating rules in the reverse order of your preference acts as a fallback. This is because in the event the browser doesn't understand your preferred value, it will silently be ignored. In Bramus's demo, he shows how we can use the CSS color function and custom properties via @property to access the p3 color space to create a "hotterpink" in place of the CSS named color hotpink
. To borrow his example, we can achieve the same thing simply by declaring the values in reverse-preference order.
.background-hotter-pink {
background: hotpink;
background: color(display-p3 1 0 0.87);
}
In browsers that support the color function, the background will be our nice bright "hotter pink". In older browsers, we'll fallback to the duller, but still nice, hotpink. While custom properties can provide more control, plenty of "fallback value" scenarios are readily managed by the cascade as this example shows. The difficulty here is that it's a little verbose, and potentially confusing to put the value you prefer the most as the last one. Brandon's suggestion of a "native function" for this did make me consider if a shorthand for this would be useful... Enter SASS.
Convenient Fallback Authoring with SASS
In Brandon's tweet, he suggested: "Something like… use-first-that-works(value1, value2, value3, valueN)
but less verbose". To me, this meant listing the values in the order you prefer the browser use them. So I decided to name the mixin prefer
, and the code is fairly straightforward:
/**
* Generates a cascade of preferred values for a given CSS property
* @param $property: css property name you wish to provide values for
* @param $values: any number of values you'd like to use, in order of preference
*
*/
@mixin prefer($property, $values...) {
@for $i from length($values) to 0 {
#{$property}: nth($values, $i);
}
}
The mixin accepts a CSS property name as a string, followed by any number of values you'd like to use in order of preference. The $property
argument is parsed as a string, and $values...
becomes a list of all the arguments that follow. Then, we simply loop through the provided arguments in reverse order to generate the correct cascading values. As a usage example, our hotter pink background from above would look like this:
.background-hotter-pink {
@prefer('background', color(display-p3 1 0 0.87), hotpink);
}
The generated code is the same as the first example. Whether this SASS mixin is easier to read, or easier to reason about than simply writing out the repetitive styles yourself depends on how you think about writing CSS, and your specific usecases, but in some situations I can see this being a little more convenient.
Notes
I should note that I did not test this with any values that contain commas. The color function example here could contain commas, but they're optional. I suspect a comma in that function would break this, in which case the mixin would need to be altered to accept values as strings and parse them as it does the $property
argument. In the brief time I explored this idea, I did not think of a usecase where commas would be strictly necessary, so I left it as is.
Implemented as a native CSS function would also aid readability quite a bit. As far as I know, there's no method in SASS to identify a specific property a mixin is being used on, so we must provide it as an argument. In plain CSS this could be simpler, as the function would come after the property name:
.background-hotter-pink {
background: prefer(color(display-p3 1 0 0.87), hotpink));
}
Whether or not CSS needs this is debatable, but as someone who pretty regularly leverages the cascade for simple fallbacks in realworld projects, I plan on using this mixin when I can to get a feel for what that authoring experience is like. If you find this mixin useful, tweet at me and let me know how you're using it!
Posted on August 26, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.