Debug panel

Close debug panel
Roma’s Unpolished Posts

Space Variable Placeholders

Published on:
Categories:
Space Toggles 2, CSS Variables 5, CSS 62
Current drink:
Camomile tea

I already posted this week once about space toggles, but today I did use a variation of the technique, where it is not strictly space toggles, but something like space placeholders. I’m not sure if I have previously seen any specific articles about this, so I decided to write about it (if you know of other articles about this — please let me know, I’ll mention them here).

The idea is: sometimes we want to make some properties extensible, which can be very useful for properties that do not have longhands like box- and text-shadow, or for properties which can accept an unlimited number of values like the same text- and box-shadow, but also content, background-image, filter, transform (which recently got an ability to specify values separately, but it might still be useful to do this with the original property) and so on.

CSS variables can be very helpful for coming up with your API, which allows controlling these properties in some very precise ways.

Here is one way I like to do this:

First pill Second pill Third pill Fourth pill

<p style="--em-shadow-top: var(--shadow-border),;">
	<em>First pill</em>
	<em style="
		--em-shadow-mid: var(--shadow-basic),;
	">Second pill</em>
	<em style="
		--em-shadow-top: initial;
		--em-shadow-low: var(--shadow-glow),;
	">Third pill</em>
	<em style="
		--em-shadow-mid: var(--shadow-basic),;
		--em-shadow-low: var(--shadow-glow),;
	">Fourth pill</em>
</p>
.example {
	--shadow-border: 0 0 0 1px hotpink;
	--shadow-basic: 2px 5px 4px -2px blue;
	--shadow-glow: 0px 0px 2px 5px lightgreen;
}

.example em {
	display: inline-block;
	border-radius: 9em;
	background: pink;
	color: #000;
	padding-inline: 0.5lh;
	box-shadow:
		var(--em-shadow-top,)
		var(--em-shadow-mid,)
		var(--em-shadow-low,)
		0 0;
}
A live example of four rounded pills that have different shadows based on the CSS variables which are applied to them.

There are a few interesting things going on in there which might look weird at the first glance, but allow me to explain!

  1. In CSS, we can see the weird comma at the end of the variable name: var(--em-shadow-top,) — this is a valid way to define an empty fallback, or a space, often used in the space toggles.

    I wrote a bit more extensively about this part in my article on cyclic toggles, but in short: whenever the variable in question has its initial value (or — it is not defined, or it is invalid at computed-value time), it would result in just an empty space, which is a valid thing to have in any part of any value in CSS. This is a very compact way of saying, “we can put something here, but it is also ok to just skip it”. If we don’t use the comma, the “initial” value of the variable would make the whole property invalid, so we have to have some fallback here.

  2. The box-shadow definition ends with 0 0. If you’d look at the HTML we have, you might already guess why: we define our variables like this: --em-shadow-top: var(--shadow-border),; — note the comma at the end. Because CSS does not allow trailing commas in lists, and we don’t know in advance which variables we could define and in which order, we want to make sure any combination of them would work.

    The easiest way to achieve this is by adding the comma either to our token definition (like to the --shadow-border), or to the places where we use the tokens (as in the example). With the 0 0 at the end of our box-shadow, we can be sure that if we’d add a comma for any of the values, things would still render properly. And the 0 0 is just the smallest valid shadow that does not really do anything. Other properties could have other ways of handling this, but often they would not even require this comma stuff, like for the content or transform values which are just separated by spaces.

The rest should be relatively obvious: we’re using tokens so we won’t need to define their values all the time, we can define the “default” values on the ancestors (on the <p> in our case), and then override them back to nothing (see --em-shadow-top: initial;).

This is also just one of the ways something like this can be handled, and there are various other cases where we could use CSS variables in this manner. Custom properties are powerful!

Please share your thoughts about this on Mastodon!