Feature detect CSS @property support

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 + 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

See the Pen CSS @property feature detection (style queries) by Bramus (@bramus) on CodePen.

~

Feature detecting @property support + 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. For reasons detailed further down, I am registering the Custom Property using JavaScript.

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?

The short answer is that you can register a Custom Property with a space as its initial-value, but that it’s not reliable across browsers. The longer answer is that 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 (bug report here) whereas Firefox (with feature flag at the time of writing) OTOH does play nice with it.

Check out this demo: it’s red (wrong) in Safari and green in Chrome 119+ and Firefox Nightly

Demo

See the Pen CSS @property feature detection (Space Toggle by Bramus (@bramus) on CodePen.

~

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.

~

Published by Bramus!

Bramus is a frontend web developer from Belgium, working as a Chrome Developer Relations Engineer at Google. From the moment he discovered view-source at the age of 14 (way back in 1997), he fell in love with the web and has been tinkering with it ever since (more …)

Unless noted otherwise, the contents of this post are licensed under the Creative Commons Attribution 4.0 License and code samples are licensed under the MIT License

Join the Conversation

2 Comments

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.