Description
In #2463 we just resolved to add at-rule(@foo)
and at-rule(@foo; desc: value)
functions to @supports, to test for whether an at-rule is supported at all, and whether it supports a given desc: value
declaration.
It was brought up in the call (and deferred for time) that we should be able to test for support of things in an at-rule's prelude as well, as that can sometimes be significant and potentially change over time. (Especially for at-rules that end in a semicolon instead of a block, as they're nothing but prelude.)
An important point brought up by @emilio (and supported with historical design context by @dbaron) is that part of the "no special-casing needed, It Just Works" feature of @supports is that we can use it to tell if something is dropped as a whole easily, but can't easily test whether some part of a construct is dropped (since that information is not currently signaled by the CSS parsers, and even if parsers are instrumented to flag a parse error, it's possible to miss spots and thus have @supports give an incorrect result).
In the case of preludes, this isn't a problem - they're either supported or invalidate the at-rule as a whole. Thus, adding detection support for them is possible. The question is just how precisely to invoke this.
To avoid any special-casing at all, I suggest we do support passing an entire at-rule in the function, but we just test whether the at-rule as a whole is dropped or not. That is, at-rule(@foo bar baz {...})
(or at-rule(@foo bar baz)
for semicolon-ended rules) is allowed, and just detects whether the rule, when parsed, is dropped or not. We do the parsing as if it was the first content in a stylesheet, so at-rule(@import "foo")
would return true.
We thus would have three parsing forms for the at-rule function:
- Just the at-rule name,
at-rule(@foo)
, which returns whether this is a recognized at-rule name at all. (This doesn't invoke any parsing, but afaik all existing impls do keep up lists of all their recognized at-rules, so this should still be a no-special-case easy check without the possibility of drifting out of sync.) - An entire at-rule,
at-rule(@foo bar {baz: qux})
, which returns whether or not the at-rule as a whole, when parsed as the first and only content in a fresh stylesheet, is valid or dropped. If the at-rule is valid but drops some of its contents as invalid, such as an unknown descriptor, this will still return true. - An at-rule name accompanied by a descriptor declaration,
at-rule(@foo; desc: value)
, which returns whether the at-rule is recognized, and the given descriptor successfully parses as part of that at-rule. This should be testable by invoking existing parsing functions in a relatively generic fashion.
Some implications:
- The second form implies that any required descriptors have to be passed as well;
at-rule(@counter-style foo {})
will fail, because @counter-style requires the 'system' descriptors and either 'symbols' or 'additive-symbols', or else the whole rule is dropped. - Also, naively the first and second forms collide grammar-wise; we tell them apart by always invoking the first form when the only non-WS contents of the function are a single at-keyword token.
- Should a trailing semicolon be allowed for the second form, when it's a semicolon-ended rule? It's not actually grammatically ambiguous with the third form (as the third requires a
desc:value
after it), but allowing it as a valid second-form invocation is a small wrinkle in parsers. I'm fine either way.