Feature detect CSS @property support



This content originally appeared on Bram.us and was authored by Bramus!

Today on Mastodon, Nils asked how to detect support for @property. While in theory you could use @supports at-rule() for this, in practice you can’t because it has no browser support (😭).

Thankfully, there’s a workaround … by trying to actively use registered custom properties and style things based on the outcome.

~

Feature detecting @property support with Style Queries

This trick involves registering a Custom Property --supported-sentinel property with a specific initial-value. When then trying to use it inside var() the resulting value will either be that initial value or the fallback you provide. In the following snippet below the value for --supported will be either 1 in browsers with support or 0 in browsers with no support for @property

@property --support-sentinel {
	syntax: "<number>";
	initial-value: 1;
	inherits: false;
}

:root {
	--supported: var(--support-sentinel, 0);
}

Using Style Queries you can then style children of :root based on the value of --supported:

/* No support */
body {
	background: red;
}

/* Has support */
@container style(--supported: 1) {
	body {
		background: green;
	}
}

The downside of this method is that the browser also needs to support Style Queries, which currently are only supported in Blink/Chromium. I’m sure Jane can remix this to use Type Grinding

Demo

~

Feature detecting @property support with a Space Toggle

A detection with broader support is to use a Space Toggle. One downside is that it requires JavaScript to register the toggler because of differences between browsers.

🤔 What’s a Space Toggle?

If you are unfamiliar with the Space Toggle Hack, it’s a hack that relies on a custom property that you toggle between two values: it’s either a space ( ) or initial. The former indicates that it’s ON and the latter that it’s OFF.

--toggler: ; /* = ON */
--toggler: initial; /* = OFF */

You use this property to generate other values: a value for when it’s ON and a value for when it’s OFF.

It relies on CSS eating spaces (e.g.   green becomes simply green) and CSS falling back to the fallback value when var() refers to a custom property that contains initial.

Behavior when it’s ON ( ):

--toggler: ; /* = ON */
--value-when-on: var(--toggler) green; /* = `  green` = `green` */
--value-when-off: var(--toggler, red); /* = ` ` or `red` = `red` */
background: var(--value-when-on, var(--value-when-off)); /* = `green` or ` ` = `green` */

Behavior when it’s OFF (initial):

--toggler: initial; /* = OFF */
--value-when-on: var(--toggler) green; /* = `initial green` = `initial` */
--value-when-off: var(--toggler, red); /* = `initial` or `red` = `red` */
background: var(--value-when-on, var(--value-when-off)); /* = `initial` or `red` = `red` */

This trick also involves registering a custom property but you give it an initial-value of a single space.

CSS.registerProperty({
	name: '--supported',
	syntax: '*',
	initialValue: String.fromCharCode(0x20), // (or just use ' ')
	inherits: false
});

In browsers with support, the value for --supported will be that space. In browsers with no support, the value for --supported will be the guaranteed initial value of initial. Yes, a classic Space Toggle indeed!

body {
	--bg-if-support: var(--supported) green;
	--bg-if-no-support: var(--supported, red);
	
	background: var(--bg-if-support, var(--bg-if-no-support));
}
🤔 Why can’t you rely on registering the Custom Property using CSS?

Up until 2023 it wasn’t possible to register a Custom Property without an initial-value. Because the CSS parser eats up spaces it wouldn’t recognize a space as the initial-value, so the following code wouldn’t work.

@property --supported {
  syntax: '*';
  initial-value: ;
  inherits: false;
}

With the spaces being eaten, the parser would think the initial-value was missing, and thus it would discard the entire registration. This got fixed at the spec level after I filed a CSSWG Issue for this: the initial-value descriptor is now optional when the syntax is set to *.

While the behavior got updated in Chrome 119 thanks to this commit, you shouldn’t rely on the CSS registration because that would exclude Chrome versions 85-118. Furthermore Safari doesn’t seem to like this CSS registration variant whereas Firefox (with feature flag at the time of writing) OTOH does play nice with it.

Demo

~

Spread the word

Feel free to repost one of the posts from social media to give them more reach, or link to this post from your own blog.

~


This content originally appeared on Bram.us and was authored by Bramus!