Using CSS can I check if a browser supports the "CSS Properties and Values API" (Houdini) @property rule

962 views Asked by At

With some browsers starting to introduce the CSS Houdini API, I was wondering if there were any ways to identify if the CSS Properties and Values API is supported with CSS alone?

With Javascript I'm able to check if the API exists: ✅

typeof window.CSS.registerProperty !== 'undefined'

Are there any equivalences native to CSS? I was experimenting with the @support rule, but this only accepts properties and values -- not 'at-rules'. So the following will understandably not work. ❌

@property --my-color {
  syntax: '<color>';
  inherits: false;
  initial-value: #c0ffee;
}

@supports ( @property : --my-color ) {
  body { background:DarkSeaGreen ; }
}

@supports not ( @property : --my-color ) {
  body { background:Crimson; }
}

☝️ CodePen Example

3

There are 3 answers

0
myf On BEST ANSWER

Apparently best we can do at this point is to pretend that support of paint worklet denotes support of CSS Typed OM @property in style sheets: unlike @property, <property-accepting-image>: paint(<ident>) can be used in @supports block.

Una Kravets uses this in her @property dev.to article:

@supports (background: paint(something)) {
  /* [Typed OM `@property` should be supported here] */
}

She also notes that this is (was) not reliable in Chromiums from version 78 up to v84.

For current state of browsers according https://ishoudinireadyyet.com/ it should be safe to use this recommendation:

ishoudinireadyyet.com table showing that all browsers supporting the Paint API also supports CSS Typed OM

It seems plausible that UAs newly adopting Houdini will release support for both paint API and CSS OM in style sheets, i.e. the Chromium v84 scenario will not happen again. (And if it will, my bet is the Typed OM will be released prior to paint worklet, so the condition will be (unnecessarily) ignored in that versions.)

0
Amirreza Zarkesh On

You can also use window.CSS.paintWorklet to check houdini api is supported or not.

1
Chester Fung On

Firefox's Support = TBC Experimental

I guess paint(...) will be far from Firefox's supports, but Firefox is already doing some experimental supports on CSS Properties and Values via layout.css.property-and-value-api.enabled and layout.css.properties-and-values.enabled.

layout.css.property-and-value-api.enabled

layout.css.properties-and-values.enabled

  • It can be turned to true in about:config to get window.CSS.registerProperty being supported.
  • It does not really mean you can use @property.
  • Using typeof window.CSS.registerProperty !== 'undefined' is not guaranteed to have the support on @property.
  • Note: Starting from Firefox 120, it is truly supported

CSS Detection

No @supports for @property

There is no @supports (@property) {...}.

You can use pure CSS or JavaScript to detect this feature is supported or not. (For pure CSS, it can just display the text to tell you it is not supported.)

Pure CSS Detection via Pseudo Element's content

  • Set a number property --num with initial value above 0. (number 3)
  • Use counter and counter-style to generate the support message with number 3 and unsupported message with number 0.
@property --my-color {
  syntax: '<color>';
  inherits: false;
  initial-value: #c0ffee;
}

@property --num {
  syntax: "<integer>";
  initial-value: 3;
  inherits: false;
}

@counter-style my-style-fallback {
  system: cyclic;
  symbols: 'Not Supported''Not Supported';
}

@counter-style my-style {
  system: cyclic;
  symbols: 'Supported''Supported';
  range: 2 4;
  fallback: my-style-fallback;
}

#result-css {
  counter-set: num var(--num);
}

#result-css::after {
  content: 'Not Supported';
  content: counter(num, my-style);
}

JavaScript Detection

No try catch block with CSS.registerProperty

  • Note: At the time I write this post, it is still not handled correctly in Chrome.

The following code

window.CSS.registerProperty({
  name: "--my-color",
  syntax: "<color>",
  inherits: false,
  initialValue: "#c0ffee",
});

might not raise any error if you have defined the property via CSS not via JavaScript.

Note: If you use registerProperty to register the property name that defined in CSS, then error might come and at the same time the property is being override.

Detection by getComputedStyle

Since every CSS properties are well defined with type and initial value, you can use getComputedStyle API to check it.

window.getComputedStyle(document.documentElement).getPropertyValue('--my-color');

In your case, the result shall be "#c0ffee". If it is not defined, the result will be "".

Combined Coding Demo for Reference

jsFiddle version

const resultByJs = window.getComputedStyle(document.documentElement).getPropertyValue('--my-color');


document.getElementById('result-js').textContent = resultByJs === '' ? 'Not Supported' : 'Supported'
@property --my-color {
  syntax: '<color>';
  inherits: false;
  initial-value: #c0ffee;
}

@property --num {
  syntax: "<integer>";
  initial-value: 3;
  inherits: false;
}

@counter-style my-style-fallback {
  system: cyclic;
  symbols: 'Not Supported''Not Supported';
}

@counter-style my-style {
  system: cyclic;
  symbols: 'Supported''Supported';
  range: 2 4;
  fallback: my-style-fallback;
}

#result-css {
  counter-set: num var(--num);
}

#result-css::after {
  content: 'Not Supported';
  content: counter(num, my-style);
}
<html>


<div>
  <h3>`@property` checking by CSS</h3>
  Result: <span id="result-css"></span>
</div>


<div>
  <h3>`@property` checking by JS</h3>
  Result: <span id="result-js"></span>
</div>


</html>

Remarks added on 2023/12/15

It has been tested that, Firefox 120 (Stable) can enable CSS Houdini using layout.css.properties-and-values.enabled = true

However, it is still not yet supported with Animation API.

Further Remarks: The support starts from Firefox Nightly 123.0a1 (2023-12-30) (64-bit)

Further Remarks: The stable support starts from Firefox 123.0 since 2024.03.02

jsFiddle version

const resultByJs = window.getComputedStyle(document.documentElement).getPropertyValue('--my-color');


document.getElementById('result-js').textContent = resultByJs === '' ? 'Not Supported' : 'Supported'


document.documentElement.animate(
  [
    { '--num': '100' },
    { '--num': '0' }
  ],
  {
    fill: "forwards",
    duration: 1000*30,
    easing: 'linear'
  }
);

let resultByJsA = window.getComputedStyle(document.documentElement).getPropertyValue('--num');

document.getElementById('result-jsa').textContent = `${resultByJsA}`.length > 1 ? 'Supported' : 'Not Supported';
@property --my-color {
  syntax: '<color>';
  inherits: false;
  initial-value: #c0ffee;
}

@property --num {
  syntax: "<integer>";
  initial-value: 3;
  inherits: false;
}


@counter-style my-style-fallback {
  system: cyclic;
  symbols: 'Not Supported''Not Supported';
}

@counter-style my-style {
  system: cyclic;
  symbols: 'Supported''Supported';
  range: 2 4;
  fallback: my-style-fallback;
}

#result-css {
  counter-set: num var(--num);
}

#result-css::after {
  content: 'Not Supported';
  content: counter(num, my-style);
}

h3 {
  display: inline-block; width: 18em;
}
<html>


  <div>
    <h3>`@property` checking by CSS</h3>
    Result: <span id="result-css"></span>
  </div>


  <div>
    <h3>`@property` checking by JS</h3>
    Result: <span id="result-js"></span>
  </div>
  
  
  <div>
    <h3> Animating `@property` checking by JS</h3>
    Result: <span id="result-jsa"></span>
  </div>


</html>