w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.5k stars 665 forks source link

[css-conditional-5] Add ability to test for at-rule preludes #6966

Open tabatkins opened 2 years ago

tabatkins commented 2 years ago

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:

  1. 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.)
  2. 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.
  3. 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:

yisibl commented 2 years ago

We have so many at rules today. It does need a good mechanism to detect if they are supported or not.

image

mirisuzanne commented 2 years ago

This is relevant for browsers that want to ship style container queries, along with a feature query for testing support. I left more details in https://github.com/w3c/csswg-drafts/issues/6175#issuecomment-1232179302, but I think thread this is actually the proper place to make progress on the issue.

Would it be ready to bring back as a proposal with agenda+, or is there more discussion that needs to happen here? @tabatkins summary above looks reasonable to me, and better than one-offs like the @supports container() syntax that we approved in the other thread.

/cc @lilles

mirisuzanne commented 2 years ago

Agenda+ to see if we can get this resolved, and/or get concrete feedback on the proposal.

css-meeting-bot commented 2 years ago

The CSS Working Group just discussed At-rule Prelude Tests.

The full IRC log of that discussion <fantasai> Topic: At-rule Prelude Tests
<fantasai> github: https://github.com/w3c/csswg-drafts/issues/6966
<fantasai> TabAtkins: earlier we defined testing for at-rules and descriptors, which we can do generically by throwing it at a parser
<fantasai> TabAtkins: we don't have ability to check if prelude of atr-ule is valid or not
<fantasai> TabAtkins: this should follow the same "throw at parser and see", but not possible atm
<fantasai> TabAtkins: especially important for at-rules that don't have blocks, e.g. @layer statement form
<fantasai> TabAtkins: you can test @layer at all, but not specifically for @rule statements
<fantasai> TabAtkins: suggest we take the first form of the at-rule syntax and allow it to take more
<fantasai> TabAtkins: parse all of that as the prelude
<miriam> q+
<fantasai> TabAtkins: as long as it passes your parser check, it's OK
<heycam> q+
<fantasai> miriam: I think this is also required for testing whether style queries are supported
<astearns> ack heycam
<fantasai> heycam: I'm wondering if there's any benefit from having this called "rule" rather than "at-rule", can we match any kind of rule not just at-rules?
<fantasai> TabAtkins: unless you know the context you're parsing in, there's only one type of rule and it's style rule
<fantasai> heycam: for those rules you can rely on selectors and individual properties
<heycam> fantasai: to summarize, three parsing forms for at-rule function
<heycam> ... one just takes the name
<heycam> ... one takes the entirety of the rule, and if anything is dropped, then it returns false
<fantasai> https://github.com/w3c/csswg-drafts/issues/6966
<heycam> ... and the third is the existing descriptor, `@foo; descriptor:value`
<TabAtkins> at-rule(@foo bar {baz: qux}),
<fantasai> fantasai: That would let you do complicated nesting structures as well
<fantasai> TabAtkins: If feed into parser, is the top-level rule kept or not. Ignoring inside things.
<heycam> fantasai: I think that would not be hepful
<fantasai> TabAtkins: more specific, would require more specific plumbing in browsers
<fantasai> TabAtkins: to report pass or failure
<fantasai> TabAtkins: and also it's more powerful than you necessarily wanted
<heycam> fantasai: I think if we want to add a full whole thing kind of syntax, then we should be parsing everything in there and if anything is dropped, you fail
<heycam> ... if you want it more limited
<heycam> ... if you want to check statement vs block, then add curly quotes
<heycam> ... as soon as you put something in there, authors will expect it's testing everything in there
<heycam> ... if we allow dropped things, authors will be confused, and we won't have an extension mechanism in the future
<heycam> fantasai: authors are going to expect that when you put a test in @supports it's everything you put in there that you're checking
<florian> q+
<heycam> fantasai: fine to add more specific syntaxes, but authors are going to expect if you add in @supports, it's checking the full thing is understood
<heycam> miriam: I agree
<heycam> emilio: yes
<fantasai> TabAtkins: That is something that was specifically called out, that bubling up invalidity is problematic
<chris> s/emilio/chris/
<heycam> TabAtkins: that was specifically called out as problematic in an earlier discussion
<heycam> fantasai: so limit the syntax
<heycam> fantasai: that's fine, if you want to do nested structures, do it properly
<astearns> ack florian
<fantasai> ^TabAtkins: then do nested structures
<fantasai> florian: We don't want @supports to be smart. Want to use the actual parser
<fantasai> florian: if we start to maintain some degree of logic, then that logic can get out of sync with reality
<fantasai> s/do nested/don't do nested/
<fantasai> s/don't/can't/
<fantasai> florian: usefulness of at-supports will decrease if we try to be smart about it, because will be less reliable
<heycam> fantasai: I think it's more important that anyything we allow in @supports ,the author can rely it's testing the whole thing
<heycam> ... I think that's more important than the concern that it might get out of sync
<heycam> TabAtkins: I disagree. author confusion is substantially better than UA lying.
<heycam> fantasai: the UA is effectively lying from the perspective of the author
<emilio> q+
<fantasai> fantasai: if you let it ignore stuff that is inside @supports check
<fantasai> astearns: This is an issue about preludes, so how about we just add preludes and not address nesting
<fantasai> TabAtkins: so we would just take the prelude, not semicolon
<astearns> ack emilio
<fantasai> emilio: I agree with TabAtkins, it's weird to suddenly pass e.g. @font-face rule, and you read the descriptors and the ones you care about you keep and the others you ignore
<TabAtkins> Authors being confused means the author can fix this, by doing it right. UAs lying means the author can't fix this; they're just screwed. UA lying is *meaningfully different* from author confusion.
<fantasai> emilio: it would require this special mode, then you have to find the places
<heycam> fantasai: I'm not arguing with you on this point
<heycam> ... if you're going to allow the syntax in @supports, then you must check all of it
<heycam> ... if you don't want that, don't allow it in the syntax
<fantasai> astearns: So proposed resolution is allow @rule preludes in the syntax, and check them for validity
<fantasai> fremy: You have at-rule in the prelude, what if at-rule is only valid inside another at-rule?
<fantasai> TabAtkins: This should still be fine, you won't be able to do any parent-child checking
<fantasai> TabAtkins: but this sub-atrule gets parsed somehow
<fantasai> TabAtkins: only problem is if you have two different at-rules that have different children with different names
<TabAtkins> s/different name/same names/
<astearns> ack fantasai
<heycam> fantasai: if I say @at-rule(@media foo), will that parse as supporting querying against foo?
<heycam> ... that's not going to cause the media rule to be invalidate, I'm asking whether you support that media rule
<florian> q+
<heycam> ... if it's going to return true for anything that appears after @media, that's not useful
<heycam> TabAtkins: [missed]
<fantasai> s/useful/useful, and it is confusing and unexpected to the author/
<fantasai> emilio: We have custom parsing for unforgiving selectors, right?
<fantasai> TabAtkins: Still, I suspect that this is a distinctly different thing because switching between selector lists isn't straightforward
<fantasai> emilio: but there is some amount of precedent, not that I love it
<fantasai> florian: for fantasai's example about media queries, it's undecideable
<fantasai> florian: because, telling the difference between whether you support querying whether running on 3D printer vs asking, you can't distinguish as an author
<heycam> fantasai: let's take a better example
<fantasai> florian: you'll get a "no", which is the correct answer
<fantasai> fantasai: consider the touchscreen MQ
<heycam> ... if the author asks do you support querying whether this is a touchscreen, and you say sure, I support every media query ever
<heycam> ... then the author will assume you support the media query
<heycam> TabAtkins: they'll get a no when they actually use the MQ
<heycam> fantasai: maybe they want to figure out some other heuristics?
<heycam> ... maybe I'll change my layout if you don't support this MQ, and cannot know from the MQ whether you support a touchscreen
<heycam> ... but you don't have the ability to check that
<heycam> ... not saying you necessarily don't need this feature, I am saying if you put it in an @supports query, the author will reasonably expect the browser will check if it understands that MQ, and we should not violate that expectation
<fantasai> TabAtkins: Anything that requires custom parsing
<fantasai> fantasai: then don't allow it
<fantasai> TabAtkins: either you can never test for preludes, or for some extremely forgiving syntaxes, this query is not going to be maximally useful
<fantasai> TabAtkins: and we can introduce a new query later
<fantasai> TabAtkins: either we don't allow preludes at all, or we accept that @media is confusing
<TabAtkins> TabAtkins: And we fix it in the future with a `media-feature()` query
<fantasai> astearns: Is that going to be acceptable, to allow testing for preludes with this possibly workaroundable thing?
<fantasai> fantasai: depends what other people think? I'm only one member of this WG
<fantasai> miriam: I agree with both of you.
<fantasai> miriam: so that's helpful!
<fantasai> florian: I'm lost about the overall discussion, but I think @supports testing of media queries is not useful given current form of media query
<heycam> fantasai: how forgiving is parsing on CQs?
<fantasai> miriam: So the problem is specific to media queries, we don't have the problem with container queries right?
<fantasai> emilio: it's same
<fantasai> emilio: also have same problem with @supports itself
<fantasai> florian: well people won't use @supports @supports
<fantasai> florian: nobody will do that one so it doesn't matter
<TabAtkins> Anything with `<general-enclosed>` in the grammar is forgiving in this way, and won't be good to test.
<fantasai> miriam: People do wwant to know whether @supports of selectors works
<fantasai> TabAtkins, yeah, but authors can't figure that out
<fantasai> TabAtkins, it's totally opaque to them
<TabAtkins> yeah, i'm just answering the question
<fantasai> miriam: SO what's the solution that gets us there?
<fantasai> florian: MQ was designed to work around the problem. When browser doesn't know how to answer it doesn't know how
<fantasai> florian: [...]
<fantasai> fremy: You can design the query so you get the support you want
<fantasai> florian: if you want a third design where you don't know whether your design can over, this is tricky
<fantasai> TabAtkins: we can let author ssolve that with custom MQ
<fantasai> TabAtkins: because that's what they do today
<fantasai> miriam: Does @else help? Means you can just attach a negation to the query itself
<florian> s/design can over/design can hover/
<fantasai> TabAtkins: top-level query is alwasy true or false, unknown is not a response
<fantasai> emilio: For @media, can't you do that? Ask for a logical combination of your query and not your query
<fantasai> emilio: e.g. @media hover and not hover, and that should be true on all browsers that support hover and not on ones that don't
<fantasai> emilio: I mean 'or'
<fantasai> emilio: so can do that with CSS already, though not beautiful
<TabAtkins> (true or false) is true, (unknown or unknown) is unknown (and thus false)
<TabAtkins> so yeah that works
<fantasai> florian: for media features yes, but not media types
<fantasai> TabAtkins: I think we have multiple ways to address the media being restrictive
<fantasai> TabAtkins: I would be very sad if, though we have ways to address it, we lost ability to do anything else that doesn't have forgiving this nature
<fantasai> TabAtkins: fantasai, can you let us do this as specified, if other feautres can let you figure out media queries
<heycam> fantasai: I can live with it, but I really do want to hear from others in the room
<fantasai> astearns: Proposed resolution is to allow testing of at-rule preludes in supports, using the prelud syntax and no nesting as yet
<fantasai> florian: I suggest we include a note that things that have forgiving parsing will say yes, and it doesn't mean that they are support
<heycam> fantasai: I think it will confuse a lot of people, but I understand we do want to do this for other things for which it does make sense
<heycam> ... my concern for CQs is that if we have to invent a new function for say state queries, they'll ask if that's supported, and we'll say yes
<fantasai> miriam: This doesn't work for the things I want it to work for
<emeyer> q+
<fantasai> miriam: and I agree it'll confuse people
<florian> q-
<astearns> ack florian
<fantasai> miriam: I wonder if we can restrict it to just the things that it's useful for?
<fantasai> TabAtkins: we could maybe do that, say it doesn't do anything on @media/@supports/@container
<astearns> ack emeyer
<fantasai> emeyer: I think Tab addressed my concern.
<fantasai> emeyer: I'm concerned we're starting to cross into territory.... I agree with Elika's point that this will be confusing in these cases
<fantasai> emeyer: if we restrict from those cases, then it's ok with me
<fantasai> emeyer: @supports is already a bit confusing, e.g. @supports (text-decoration: blink)
<fantasai> emeyer: I was concerned that we would introduce a class of problems that authors would be *much* more confused about
<fantasai> emeyer: but I believe the syntax restrictions would address my concerns
<fantasai> astearns: what would it address?
<heycam> fantasai: anything with forgiving syntax
<fantasai> TabAtkins: we have 27 at-rules ...
<fantasai> florian: Anything with <general-enclosed>
<TabAtkins> s/27/20-something/
<fantasai> florian: Alternative is if you hit <general-enclosed>, return false
<fantasai> TabAtkins: that's specialized parsing
<fantasai> heycam: so it's that the condition, if you use an at-rule(@media ...) it's not invalid, it's just false?
<fantasai> florian: would prefer invalid, so if we figure out how to solve we can make it work
<fantasai> astearns: I've lost state of proposed resolution
<TabAtkins> Proposed resolution: allow testing at-rule preludes. Exclude @media, @supports, and @container from this form fo the test; they're invalid to use.
<TabAtkins> (Generally, anything using <general-enclosed>, but for now it's those three.)
<fantasai> florian: Can you clarify, they're invalid, does that mean they return false or they fail to parse?
<fantasai> TabAtkins: they make the rule invalid
<fantasai> florian: what do you mean by "the rule"
<heycam> fantasai: it's as if you wrote foobar() instead of at-rule()
<heycam> TabAtkins: foobar() is a false query
<fantasai> astearns: We will work out what the remaining proposal is in the future for specific syntax and behavior in a summary ocomment form TabAtkins
<fantasai> TabAtkins: I gave the proposed resolution, ignoring that bit about clarifying invalid
<fantasai> astearns: Let's get it written out, and then come back to it
<fantasai> astearns: because I've had several ppl ask that we get to Toggles!
<florian> "@support at-rule(@media foo) or (display:block) { :root{ color: blue ) }" What does that do, blue or not blue?
mirisuzanne commented 2 years ago

@emilio after the meetings, you mentioned it might be possible to do strict parsing of query syntaxes inside @supports - eg failing on unknown queries? Does that seems like a viable approach that doesn't require too much special casing?

lilles commented 1 year ago

@emilio after the meetings, you mentioned it might be possible to do strict parsing of query syntaxes inside @supports - eg failing on unknown queries? Does that seems like a viable approach that doesn't require too much special casing?

Could we make <general-enclosed> and parts of media/container queries that are unknown, including unrecognized media type fail the @supports at-rule() query?

We resolved on a similar behavior for forgiving selectors for @supports selector().

mirisuzanne commented 1 year ago

Agenda+ to see if we can resolve on @lilles suggestion above. I think it would be a significant improvement, if we can do it.

mirisuzanne commented 1 year ago

Sorry, I was over-eager putting this on the agenda. It looks like where we left off after TPAC, we still need to re-form the basic proposal - and then we can include this update to the proposal.

bramus commented 2 months ago

I’m seeing an uptick in requests from authors for this (E.g. [1], [2], [3], but there are more). One thing that dawned on me is that style queries is another special case here because of the style() function, which is not a prelude.

Of the three parsing forms proposed by Tab, the 2nd one would allow checking that.


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.

What if we renamed that one to at-rule-name()? It’s fine to rename, as nothing of this is specced yet and nobody’s shipping this.

I’m also fine with dropping the third proposed form here, and have authors test either only the at-rule’s name or an actual full rule. It would prevent that potential ; syntax-collision

benface commented 2 months ago

Oh wow I forgot about this issue when I opened https://github.com/w3c/csswg-drafts/issues/10829

yisibl commented 1 week ago

If the property in @position-try contains !important, what should the result return?

CSS.supports(`at-rule(@position-try; inset: 20px)`) // => true
CSS.supports(`at-rule(@position-try; inset: 20px !important)`) // => true or false?

It is invalid to use !important on the properties in the . Doing so causes the property it is used on to become invalid, but does not invalidate the @property-try rule as a whole. https://drafts.csswg.org/css-anchor-position-1/#accepted-position-try-properties