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.
New post: How to feature detect support for @property in CSShttps://t.co/SuXh83X719#css #featuredetection #atproperty
— Bram.us (by @bramus) (@bramusblog) July 3, 2024
~
🔥 Like what you see? Want to stay in the loop? Here's how:
In fact, there is another way to detect support for the @property CSS at-rule using a paused keyframe animation (https://observablehq.com/@quadbits/detecting-support-for-property-css-at-rule).
This method should work in all browsers that support CSS variables.