Skip to main content
Photography of Alvaro Montoro being a doofus
Alvaro Montoro

Fullstack Developer

When CSS Says 'Yes' but Browsers Say 'LOL No'

CSS treats @supports as context-blind, and that leads to surprising (and misleading) behavior in real browsers.

css webdev

According to the CSS specification, the @supports at-rule must be placed at the top level or nested inside another conditional group at-rule. However, browsers also allow code like this (which, at least in theory, is not valid):

.my-class {
  @supports (property: value) {
    ...
  }
}

And that's confusing.

Either browsers should ignore the nested @supports entirely (or always execute it, since browsers are often forgiving with HTML and CSS), or they should apply it relative to the nested-in rule. Applying it as if it were at the root level while visually nesting it inside a selector can be misleading.

Take this example:

li::marker {
  @supports (content: " - ") {
    content: " - ";
    color: red;
  }
}

This works in most browsers. Chrome, Safari, and Firefox support ::marker, and they all support content: " - ". But here's the catch: Safari does not support content inside ::marker.

With the code above, Chrome and Firefox render a red " - ", while Safari renders a red circle instead.

The confusing part is that the @supports condition succeeds even though the declaration is not actually supported in that specific context.

To be fair, this is probably less about browsers "moving" (or more accurately "parsing") nested @supports rules to the top level or in general context, and more about how @supports itself is defined. The feature checks if a declaration is generally valid, not if it is valid for a specific selector or pseudo-element context.

Maybe one solution would be to extend and (or introduce a new operator or function) so @supports checks can validate combinations rather than independent features. For example:

@supports selector(::marker) and (content: " - ")

or

@supports selector(::marker) xand (content: " - ")

or

@supports rule(::marker { content: " - " })

That would allow to test if a declaration truly works within a given rendering context, instead of only checking if the syntax is recognized.

Another (less ideal) solution would be for browsers to be less "forgiving" and never execute nested @supports rules. But that would break things.

Note: I know container and style queries may be a workaround, but they only have partial support and can only check for custom properties, not declarations (at least at the moment). Their use could help but wouldn't remove the misleading/limited nature of nested @supports.

Article originally published on