Skip to content

[css-contain] What is the migration path for Container Queries? #6175

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mirisuzanne opened this issue Apr 2, 2021 · 27 comments
Closed

[css-contain] What is the migration path for Container Queries? #6175

mirisuzanne opened this issue Apr 2, 2021 · 27 comments

Comments

@mirisuzanne
Copy link
Contributor

While at-rules provide their own test for positive support, there is currently no way to test for lack of at-rule support.

With the introduction of media-queries it became common practice to start with a reasonable fallback outside the query, and add progressive enhancement inside the query. That progressive approach also works with some container-queries (see Andy Bell's example), but it falls apart if an author wants to use media-queries as the fallback. Ideally, authors would be able to test for the lack of container-query support, and only apply media-queries when CQ is unsupported (see @eeeps codepen attempt).

If all browsers release 1D containment (or any other new property/value syntax) at the same time as container queries, Eric's approach should work:

@container {
  /* progressive enhancements */
}

/* query negative support for the related new property/value */
@supports not (contain: inline-size) {
  @media (...) { /* fallback media-queries */ }
}

It's not likely that any browser would release @container before releasing inline-size containment, but it is possible a browser would release 1d containment first. If that is the only new syntax to test, it could result in some false negatives: browsers that support both inline-size & @media, but not @container, would miss out on the fallback media-queries. That's not ideal.

I think the only way to ensure this migration path works smoothly is to include container-query-specific syntax that can be tested by @supports. The inline-size value might work for that, but only if browsers are careful to implement both features together.

@tabatkins
Copy link
Member

We've talked about wanting to be able to test for at-rule existence in @supports in the past (I can't find the issues for it right now), but it might very well make sense to just allow a trivial "does this at-keyword look like something you support?" test, like `@supports at-rule(@container) {...}

@mirisuzanne
Copy link
Contributor Author

Both of our solutions exclude the long list of browsers supporting @media without support for @supports. In your case, browsers would also have to implement @supports at-rule() fairly broadly before @container is available, or it's not terribly useful. In order to use either pattern, authors would be hiding the media-query fallback from a long list of previously-supported browsers. Adding new support syntax might be a good idea long-term (also for layers and scope?), but in the immediate case it seems like it would make the issue more dramatic?

@tabatkins
Copy link
Member

True, but fixing it "better" now makes the next time this problem comes up less troublesome. ^_^

@mirisuzanne
Copy link
Contributor Author

mirisuzanne commented Apr 2, 2021

Right, I absolutely support the feature. I'm not sure it solves the immediate issue. :)

That feature would be more powerful if it can also test support for the at-rule condition, like @supports at-rule(@media (prefers-reduced-motion: reduce)) {...}. As a bonus, I could infinitely nest @supports at-rule(@supports at-rule(@supports at-rule(…))) {…}, and that would keep me entertained for at least 20 minutes.

@SebastianZ
Copy link
Contributor

Just for reference, with the syntax I suggested in #2463 at-rules with descriptors and with nested rules would be covered for testing via @supports.

Sebastian

@mirisuzanne
Copy link
Contributor Author

So a few syntax options that I've seen in the comments here or linked…

/* possibly using a function like at-rule() or rule() */ 
@supports (@container) { … }
@supports (@container (width > 20em)) { … }
@supports (@container, width > 20em) { … }

This would certainly be helpful for things like @container and new @media features.

@SebastianZ your linked syntax also includes a style block, since it's mainly concerned with testing descriptors inside a rule. Not clear if that's meant to be required, or part of what you're suggesting here?

@SebastianZ
Copy link
Contributor

@SebastianZ your linked syntax also includes a style block, since it's mainly concerned with testing descriptors inside a rule. Not clear if that's meant to be required, or part of what you're suggesting here?

The syntax is meant to take any kind of at-rule to let the UA do a simple syntax pass to check whether the at-rule is supported or not. So, in the simplest case you'd then have something like this:

@supports (@container { * { color: wheat; } }) {
  ...
}

@mirisuzanne
Copy link
Contributor Author

There's a lot of power in that, but I think ideally the simplest case could be a bit more focused on the at-rule and its arguments. Maybe if blocks were allowed to be empty?

@supports (@container {}) { ... }
@supports (@media (prefers-reduced-motion: reduce) {}) { ... }

@SebastianZ
Copy link
Contributor

... Maybe if blocks were allowed to be empty?

@supports (@container {}) { ... }
@supports (@media (prefers-reduced-motion: reduce) {}) { ... }

Actually, they are, so that is totally valid syntax.

Sebastian

@una
Copy link
Contributor

una commented Apr 7, 2021

+1 to @mirisuzanne's suggestion of:

@supports (@container {}) { ... }
@supports (@media (prefers-reduced-motion: reduce) {}) { ... }

This is definitely needed and useful for negation to apply styles when progressively enhancing container queries:

@supports not (@container {}) { ... }

This is more resilient than @supports not (contain: inline-size) {...} because inline containment could be implemented separately of @container.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-contain] What is the migration path for Container Queries?.

The full IRC log of that discussion Topic: [css-contain] What is the migration path for Container Queries?
github: https://github.com//issues/6175
miriam: This issue started abotu container queries. A few potential ways authors could test for them. Using @supports and checking for something like contain-inline-size. Not entirely reliable.
miriam: If browsers impl 1d separately. With @rules provide positive case but can't test for negative, lack of support
miriam: Lack would be ideal here and for several other upcoming @rules
miriam: That lead us to a previous proposal from Sebastian. Possibility of doing a supports check on an @rule. Adding syntax. Una summerized at end of thread. Putting @rule inside @supports
emilio: Prefer dedicated function. Some @rules only valid in some parts of stylesheet. Not easy to hook into generic parsing algo.
emilio: We added a selector function to test for selectors. I think a MQ like container query function would be fine with MQ inside
miriam: Generically for all @rules so we don't add new funct for each rule?
emilio: We don't know how future @rules will handle stuff inside them. Separate function with syntax I think is a bit more; how would you test for nesting? Just a style rule inside with random stuff? Seems, I don't know. Maybe fine
miriam: Part of issue I see is this feature is the most powerful the farther away...we want this supported long before the @rules we add otherwise you lose utility
q+
emilio: I think sep function is most straightforward for this. Impl more generically may be fine for rules we have. For @import would be a bit annoying. Blink, WK, and Gecko trigger import loads from parsing and it's not valid everywhere.
myles: Hard to say in future we'll never add statefulness ot @rules. I don't think @rule will be valid in any context will be always true
dbaron[m]: When designing @supports one intention is adding for @rules would be via a function
dbaron[m]: One of the interesting things that comes up with @rules is they often have rules for how to ignore bits of syntax inside of them. I think @supports has to key off of is this @rule valid in a way that it will produce something. And @media rule with invalid MQ that would get ignored would still be supported by @support definition
emilio: Good point. Invalid media lists just get parsed.
ack gregwhitworth
emilio: That generic way doesn't work. PRefer specific functions for things. Maybe @-rule and take a keyword to see if browser understands in any wya
dbaron[m]: Not sure how useful to check do you understand thiskeyword vs entire @rule
emilio: The same way we have @supports(selector) and takes a list we'd have @supports(media and a media definition inside it that's specific on when you can consider supported.
dbaron[m]: I hadn't read whole issue, but examples in comments I'm not sure what' intended by some of the examples. Multiple interpretations
The example I was commenting on was: @supports (@media (prefers-reduced-motion: reduce) {}) { ... }
florian: For function emilio suggested with care dbaron[m] suggested we could do something like that if it plugs into some part of parser. @support doesn't have dedicated code path. You plug into existing machinery
emilio: True.
emilio: Saying we should...if you do at @rule level then what dbaron[m] says applies. @media(garbage) is valid. We should say it prses but doesn't have fallback
dbaron[m]: Good reason for separate @supports for media functions. But not sure devs will understand why @rule doesn't work
florian: So far I don't htink has been possible to tell UA which isn't on a media type and UA which doesn't parse
emilio: Observable. If you don't parse the serialization returns not all
emilio: Can't from CSS itself but you can from JS
emilio: And MQ5 changes that but no one impl it yet
florian: Thinking for example like features like Grid which is not CSS Grid. It's a bool and everybody returns 0 because no browser is in a terminal.
emilio: I think we parse that. Same with color and other things that are matched fixed. We parse it.
florian: I think prop is manigable but have to be careful along the way
astearns: Prop you're referring to?
florian: @supports function dedicated to media types or expressions
emilio: And fix for container
miriam: Was I hearing 2 approaches? @supports rule and then rule name and other is @supports expression and that's separate features?
emilio: Rather then @supports rule it's a function. If it's empty you return if you support rule and if has content you parse and try and return
astearns: I'm confused emilio.
astearns: I agreed with miriam interpretation. Have an @rule function in an @supports which would say if you support @rule
emilio: That's another option
emilio: Is it useful tos ay do you support @container as is or do you want to say do you support htis container condition?
I had been thinking of things like @supports at-rule(@container ... {}) {} and @supports media-expression((width: 200px)) {}
though possibly rule() rather than at-rule()
astearns: For @container do we need expression checking? Or just if @container rule?
miriam: Both is useful. knowing if rule is supported at all is most useful
emilio: @supports and anything else immediately returns false. We can define a syntax. When browser impl container queries the syntax can be used anywhere.
emilio: A generic @supports @rule would be more generic
florian: Concerned it would work at first and problems later
florian: If you say I support this @rule authors will assume it works with what they want in the rule
emilio: I think given we've determined want one for media expressions one for container expressions makes sense
miriam: And if had container function that could accept an expression and you did @supports not-container function that would give you...?
emilio: I think that should work now and browsers should do right thing now. I fyou write @supports not whatever we should parse the thing inside. We we don't we should fix that
astearns: Wondering...talking about @rule function but also a media expression function and container expression function. Wonder if rule could take multi param where first is rule type and following are expressions valid in that rule
emilio: Problem is you don't know what future @rules will support
florian: Some have most sytnax before opening { and some it's within {
dbaron[m]: And you end up inventing an additional syntax that is not what you're testing for support for
https://codepen.io/mirisuzanne/pen/VwPVpeK
myles: IN @fontface we added descriptors. Would htis prop allow authors to determine if ascent-override is supported?
emilio: There's a good question about if existing syteax should support parsing descriptors
dbaron[m]: Could add a descriptor function. Or we could say they're valid in there b/c similar to prop
miriam, you have a typo, it's @supports, not @support. Then it's green
florian: Should keep them separate. A bit of name collision
emilio: Right. We can have function that takes fontface descriptor. Proposal as is doens't address but trivial to add
florian: Descriptor with first param as a rule and second is descriptor with same syntax as properties?
emilio: Resonable. Or add fontface-descriptor
myles: Don't need to design new feature
s/feature/feature on the call/
astearns: Yes, we're far afield from original question. But if want to do right for container and container expressions need to consider how would look for other @rules
emilio: Perhaps go to issue and flesh this out for next week
emilio: Proposing syntax for container and media; precise syntax
astearns: For a function that tells you if @rule is supported and other to tell you if expressions are supported
emilio: Yeah. Not clear if you want the is the @rule supported at all. Maybe footgun. Maybe just media, container, fontface, and so on
astearns: Okay
miriam: Works for me
astearns: Additional comments?
plinss: Random thought from miriam original comment on negative case. Generic @rule that's @else which is only for if previous @rule doens't apply
fantasai: There's an issue on that
TabAtkins: On the generic when/else. not the else/if you can tack to anything
plinss: This would be @container else
florian: Else proposed would be @support and @media but not designed to work on any. @fontface else isn't clear
plinss: Sure. Some it doesn't make sense for. It could be openended. Don't have to disallow but it would never apply for @fontface unless browser doesn't support it
astearns: I'll take action to find the else issue and point it to this one
astearns: Anything else on this?

@lilles
Copy link
Member

lilles commented Apr 21, 2021

Feels a bit inconsistent not to require at-rule() or something. You don't need more than one lookahead token to see that this is an at-rule and not a declaration, but still.

@mirisuzanne
Copy link
Contributor Author

mirisuzanne commented Apr 21, 2021

@lilles My understanding of the discussion was that we're leaning towards individual functions for each at-rule:

@supports not media(width > 40em) { … }
@supports not container(min-width: 40em) { … }

(Here's a codepen demo of how it might work for Container Queries)


There was also mention of an @else rule that can be appended to any conditional block:

@container (min-width: 40em) { … }
@else { /* condition is negative */ }

And, while I like that idea, I don't think it does what we want here. I would expect @else to apply when the condition is negative (width is less than 40em), not when the condition is lacking browser support. I'm not sure we want to conflate those.

@lilles
Copy link
Member

lilles commented Apr 21, 2021

Sorry, I wasn't there and posted before I saw the notes. Separate functions sgtm.

@emilio
Copy link
Collaborator

emilio commented Apr 21, 2021

So I think some of the concerns @mirisuzanne mentioned (of needing to support this condition way before @container works) are really an issue, but only because browsers disagree on this case:

CSS.supports("not foo()")

That returns true in Firefox (as I'd expect, fwiw), but false in Chrome and Safari. So you can't use @supports not container(..) unless they implement the new thing. We should probably get interop on that. My understanding given the supports syntax in https://drafts.csswg.org/css-conditional/#typedef-supports-condition is that this is a Chromium / WebKit bug. Does it match yours @lilles?

Now with that said, in terms of fixing this, yeah, the proposal was on the lines of:

@supports at-rule(>)

To test support for an specific at-rule. @frivoal raised the concern of it being a bit footgunny in cases we extend the syntax of the at rules, so I think this was the most controversial. Then there was:

@supports container(-expression>)

And:

@supports media(-query>)

And these need specific definitions because has a clause that would parse all media queries successfully (I think this is a mediaqueries-4 feature which we weren't implementing).

@tabatkins
Copy link
Member

My understanding given the supports syntax in drafts.csswg.org/css-conditional/#typedef-supports-condition is that this is a Chromium / WebKit bug. Does it match yours @lilles?

Firefox's behavior is intended to be correct, but I note that @supports does not define what to do with an unknown value that's escaped to the top level (@media does: https://drafts.csswg.org/mediaqueries-5/#evaluating). That needs fixing.

And these need specific definitions because has a clause that would parse all media queries successfully (I think this is a mediaqueries-4 feature which we weren't implementing).

If you don't implement that, fwiw, then it's not safe for authors to use the new at-rule() or container() or whatever functions, since they'll invalidate the entire rule in older versions. :/

@lilles
Copy link
Member

lilles commented Apr 21, 2021

CSS.supports("not foo()")

That returns true in Firefox (as I'd expect, fwiw), but false in Chrome and Safari. So you can't use @supports not container(..) unless they implement the new thing. We should probably get interop on that. My understanding given the supports syntax in https://drafts.csswg.org/css-conditional/#typedef-supports-condition is that this is a Chromium / WebKit bug. Does it match yours @lilles?

I think that foo() is which is unknown, and not unknown is unknown, and I couldn't figure out how uknown should be handled.

@emilio
Copy link
Collaborator

emilio commented Apr 21, 2021

Hmm, we don't have the "unknown" concept. We just treat all future syntax as unsupported (link). I agree that the spec seems at least weird. Is there an objection of adopting the Firefox behavior here? Anyhow should probably be moved to a separate issue.

@mirisuzanne
Copy link
Contributor Author

My codepen test of a @supports not container(min-width: 0) block seems to be rendering in both Chrome & Firefox, but not Safari.

@Loirooriol
Copy link
Contributor

Loirooriol commented Apr 26, 2021

It's funny:

Code Firefox Chromium WebKit
not foo() true false false
not foo(bar) true true false
not (foo()) true false true
not (foo(bar)) true true true

So I guess authors can use @supports not (container(min-width: 0)).

@mirisuzanne
Copy link
Contributor Author

I think that's a fine caveat/hack for authors to ensure better backwards-compatibility, but it would also be good to get these lined up across browsers moving forward. For the sake of future conditions, it would be good for all of these to evaluate as true.

So in my mind we'd be looking for two resolutions here:

  1. Any unknown supports conditions evaluate as unsupported
  2. Add a container() function for testing support of specific container-query conditions

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-contain] What is the migration path for Container Queries?.

The full IRC log of that discussion Topic: [css-contain] What is the migration path for Container Queries?
github: https://github.com//issues/6175
miriam: Talked about this last week
miriam: Went to thread.
miriam: Prop to add a container function to supports similar to selector in that you can test a container query property value pair
miriam: Also it would be good to get consistency on how unknown functions or tests are handled
miriam: Other prop is unknown supports conditions evaluate as unsupported
Rossen_: Thank you
Rossen_: With that proposal, are there any other opinions hwere?
TabAtkins: Sounds good
miriam: Prop 1: Any unknown supports conditions should evaluate as unsupported
TabAtkins: Rephrase: unknown support condisions should work the same as unknown media conditions
florian: Clarification, they don't eval to false which is a weird not bool
TabAtkins: Eval to false at top value. Unknown prop as unknown but it doesn't define top level unknown so becomes false
emilio: Do we want @supports NOT [unknown] true or false?
TabAtkins: Same as MQ for unknown things. NOT [unknown] is unknown
miriam: Don't think that works well
emilio: Right. Not great. Can't use @supports NOT container b/c will never match. Returns true for browsers w/o container
miriam: Want it to eval true when negate it
TabAtkins: There's unknown which is syntax we don't understand and syntax that's false b/c you don't impl the thing yet
miriam: To be backwards compat we need unknown to eval true when you negate it so it works with browsers that don't understand container
dbaron[m]: Argument that @supports should be different for MQ. Unknown @supports is like an unsupported feature
Rossen_: We have folks starting to drop off
(And I think I was agreeing with miriam.)
Rossen_: Sounds like we want to take this back to the thread, flesh it out, and bring it as first topic this week
TabAtkins: Threat got confused with impl doing weird things. We're past that, just need to discuss on this

@mirisuzanne
Copy link
Contributor Author

@tabatkins I think it's essential that @supports behave different from @media in this case, because evaluating if new syntax is "known" or "unknown" is the entire purpose of the at-rule – and in many cases "negation" is the most useful test. That is especially true with new at-rules, since they provide their own positive test, and will always require new functional syntax in @supports.

When we do add new functions to @supports itself (and I forsee several more functions on the way), we get one of two results:

  1. If unknowns can't be negated (not foo() == false), the new syntax gives a false negative in old browsers -- there is no way to write a useful fallback
  2. If unknowns can be negated (not foo() == true), the new function works as expected everywhere, backwards-compatible in any browser that understands the basics of @supports -- fallbacks "just work"

I don't see how option 1 provides any useful benefit?

@tabatkins
Copy link
Member

Yeah, my original intention was that they should be the same - they both use Kleene 3-value boolean logic, where "unknown" negates to "unknown", and if the whole thing evaluates to "unknown" it is treated as false.

But I see the logic in treating an @supports unknown as just plain false, because the supports queries aren't nuanced choices from equally-likely options, like MQs are. If you don't understand a supports condition, it's likely you don't understand the feature it's testing support for either. (That can be wrong, but it's much more likely to be right than not.) So yeah, it's much more likely to give a correct result if it's just a plain false.

So I agree, we should remove the "unknown" value from supports conditions, and just treat unknown things as false.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-contain] What is the migration path for Container Queries?, and agreed to the following:

  • RESOLVED: Have unknown @supports expressions evaluate to false for all @support rules
  • RESOLVED: Create a container function that can test if @supports checks for a particular container query
The full IRC log of that discussion Topic: [css-contain] What is the migration path for Container Queries?
github: https://github.com//issues/6175
miriam: Talked a couple weeks ago. confusion on intent. Going for @supports should always treat unknowns as unsupported
miriam: Allows new funt and testin work backward compat
miriam: 2 resolutions. 1) any unknown supports eval as unsupported 2) add container funt for testing support of specific container conditions
florian: When you say treat unknown as unsupported at top level, you mean immediately?
miriam: Yeah
miriam: Being able to negate it and have it return true is essential here
TabAtkins: Good with this
astearns: Changing behavior for all supports rules?
miriam: Right now not interop on this
miriam: Chart in the thread of current handling
astearns: Thanks
TabAtkins: Spec is unclear. Does not define. Was intended to be different, but Nina convinced me this is better
florian: Didn't we have prop for unified syntax for combo of media and supprot queries?
TabAtkins: That's my when/else proposal. We'll deal with that when it comes up
florian: Okay. Might be a problem, but not as important as containers
astearns: Prop: Unknown at-supports evaluate to false and add that to spec for all supports rules
florian: For this use case it's right. Will it always be or should we specialize to supports query for containers?
miriam: I can't see situation for other behavior. Any new type of check we add to at-supports to determine if supported need it previously returning false
florian: Add new feature and query together, yes. Support syntax for things that predeated abaility to detect then no.
TabAtkins: I think consider future longer then past. A supports query moving forward that you don't understand is for a new feature you don't understand
astearns: A bit concerned we haven't run across this lack of interop. Are people not using not was supports?
miriam: Ran into this with selector
florian: With selectors, do we not want opposite behavior?
astearns: In this case opposite as @supports nad @supports not eval to unknown
florian: An unknown stays unknown until top at which point it becomes false
miriam: Why would we want that?
+1 to miriam
TabAtkins: Same as a feature. If you don't understand enough to evaluate you don't understand to use.
florian: Okay. Maybe I'm not thinking correctly. Defer to TabAtkins
astearns: Prop: Have unknown @supports expressions evaliate to false for all @support rules
RESOLVED: Have unknown @supports expressions evaluate to false for all @support rules
astearns: Do we also need to resolve for the @supports for specific features
miriam: Looking for a container function in @supports that accepts container query query conditionals
astearns: Is it the full syntax? Or a subset?
miriam: I think it should accept any...hmmm
miriam: Maybe it should be one query at a time and you can string together multiples of the function
miriam: Accepts single conditional
astearns: Had not thought threw this. Possibel we'll have new things you can add to funct over time such that an instance may or not eval based on state of impl?
miriam: I expect we will add additional feature queries over time
astearns: That seems to me argument to string together multiples. Maybe. maybe not
miriam: Makes sense and makes simpliest case simplier. @container and a singe query. Makes sense to me
astearns: Other opinions?
astearns: Prop: Create a container function that can test if @supports checks for a particular container query
astearns: Objections?
RESOLVED: Create a container function that can test if @supports checks for a particular container query
astearns: I expect once this is specced we'll have more questions

AtkinsSJ added a commit to AtkinsSJ/serenity that referenced this issue Jan 19, 2022
This is in line with this recent change to Conditional-3:

> Removed the “unknown” value in CSS feature queries’ boolean logic,
> defining unrecognized syntaxes as “false” instead.
> w3c/csswg-drafts#6175
linusg pushed a commit to SerenityOS/serenity that referenced this issue Jan 19, 2022
This is in line with this recent change to Conditional-3:

> Removed the “unknown” value in CSS feature queries’ boolean logic,
> defining unrecognized syntaxes as “false” instead.
> w3c/csswg-drafts#6175
LucasChollet pushed a commit to LucasChollet/serenity that referenced this issue Feb 3, 2022
This is in line with this recent change to Conditional-3:

> Removed the “unknown” value in CSS feature queries’ boolean logic,
> defining unrecognized syntaxes as “false” instead.
> w3c/csswg-drafts#6175
LucasChollet pushed a commit to LucasChollet/serenity that referenced this issue Feb 3, 2022
This is in line with this recent change to Conditional-3:

> Removed the “unknown” value in CSS feature queries’ boolean logic,
> defining unrecognized syntaxes as “false” instead.
> w3c/csswg-drafts#6175
@mirisuzanne
Copy link
Contributor Author

Not sure why I marked this as not needing edits. The current status seems to be:

@lilles noted recently that we might want a way to test for style() queries specifically, before launching that feature. In order to do that, we either need to flesh out the details of a container() function, or a more powerful variant of the at-rule() function that allows testing entire rule blocks. My instinct is that we likely want the latter, but maybe that needs more discussion?

@mirisuzanne
Copy link
Contributor Author

This was less necessary for the initial draft of container queries, which can be tested by checking for support of the new properties/values, eg:

@supports (container-type: inline-size) { ... }

Since style queries don't involve any new properties, a more general solution is required.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants