Skip to content
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

[css-contain-3] Provide a syntax to query a specific container-type #6393

Closed
mirisuzanne opened this issue Jun 17, 2021 · 17 comments
Closed

Comments

@mirisuzanne
Copy link
Contributor

The draft Container Query spec describes the container query syntax in two parts:

@container <<container-name>>? <<container-query-list>> {
  <<stylesheet>>
}

Which resolve in two distinct steps:

  1. container selection: Find the right container to query (nearest ancestor container, after optionally filtering by <<container-name>>)
  2. query evaluation: Evaluate each feature in the <<container-query-list>>

If no eligible container is selected, the query evaluates to false.

If the selected container in step 1 is not a valid container type for the query features used in step 2, then the query evaluates to unknown. For example, if we try to query a block-size container-type about its inline dimensions, that measurement is unknown.

That means authors will likely also want to filter and select containers based on container type – eg find the nearest inline-size container in step 1, in order to query its width in step 2. And, since a <<container-query-list>> could combine multiple different features, we might need to select for more than one container type.

Since container-names are custom identifiers, and container types are built-in keywords (with potential for new additions later on) we'll need some way to distinguish them — either by requiring dashed-idents for names (not a great option), or by adding distinct syntax for selecting each one in the @container rule. Something like:

@container <<container-name>>? type(<<container-type>>+)? <<container-query-list>> {
  <<stylesheet>>
}

@container sidebar type(inline-size style) (width > 30em) and (--card: small) {
  <<stylesheet>>
}
  • Is there a better approach than adding a type() function, or a better name for that function?
  • Do we need clearer delineation between the selection & query aspects of the @container rule?
@mirisuzanne
Copy link
Contributor Author

Maybe something that reflects the container shorthand syntax?

@container (inline-size style / sidebar) (width > 30em) and (--card: small) {
  <<stylesheet>>
}

@andruud
Copy link
Member

andruud commented Jun 18, 2021

For the shorthand-like proposal, I assume it should be possible to only specify a name or only specify a type? In that case we would need to exclude size, inline-size etc from the <custom-ident>of <container-name>, and then we're back at the problem of magic keywords.

better approach than adding a type()

Validate its existence somewhat by also having name() for name-based selection? 🙂

clearer delineation

Maybe it helps to demand parens, i.e. make the grammar <media-in-parens> (or container-in-parens) rather than <container-query-list>:

<container-selection> = <container-name> | [ name(<container-name>+)? type(<container-type>+)? ]

@container <container-selection>? <media-in-parens> {
  <stylesheet>
}

Although one thing to watch out for here is that if fn() is valid for both the selection and the query, we get a problem with: @container fn() {}. So maybe <media-in-parens> is not enough.

@fantasai
Copy link
Collaborator

I like the idea of keeping the name/type syntax in the container shorthand and in the @container rule consistent, as mentioned in #6393 (comment) . Maybe that means using the container syntax in @container or maybe it means shifting both to a new syntax that is a good fit for both contexts, but either way I think the consistency would benefit authors.

@mirisuzanne
Copy link
Contributor Author

mirisuzanne commented Sep 24, 2021

A few options for a shared shorthand…

Our current container property syntax relies on consistent order, and the / divider (eg types / names). In order to make that work with both sides optional, the delimiter needs to be used for all names, even when no type is given. Either using punctuation or a keyword:

/* using the current `/` divider */
@container inline-size / my-name ( width > 30em ) { … }
@container inline-size ( width > 30em ) { … }
@container / my-name ( width > 30em ) { … }

/* using a single keyword, still ordered */
@container inline-size as my-name ( width > 30em ) { … }
@container inline-size ( width > 30em ) { … }
@container as my-name ( width > 30em ) { … }

But that order was based on the assumption that a type is required, and names are optional in the shorthand. That assumption is clearly false in the @container rule, but it may also be wrong for the shorthand property. If we consider re-using named containers for something like a scroll-timeline (see #6674), it doesn't seem like types should be required in those situations. The same is likely true for style queries, which should work on any container. That makes me want a more flexible syntax, where the order is less important. That could be done with two keywords ('for' <types> || 'as' <names>):

@container for inline-size as my-name ( width > 30em ) { … }
@container as my-name for inline-size ( width > 30em ) { … }
@container for inline-size ( width > 30em ) { … }
@container as my-name ( width > 30em ) { … }

But the more I stare at this, the more tempted I am to just use dashed-idents for names, and allow the two values to mingle completely:

@container inline-size --my-name ( width > 30em ) { … }
@container --my-name inline-size ( width > 30em ) { … }
@container inline-size ( width > 30em ) { … }
@container --my-name ( width > 30em ) { … }

That might also allow us to express and/or combinations, maybe with the / divider between container-selection and query logic (if that division needs more clarity). Something like:

@container inline-size and --my-name / ( width > 30em ) { … }
@container (--my-name or --other-name) / ( width > 30em ) { … }

We could consider adding parenthesis around any of these options, if it helps.

@fantasai
Copy link
Collaborator

We don't really use preposition syntax markers outside of functions (at least not yet), and using --custom-property syntax here also feels kinda weird. What about:

container: <'container-name'>? [ / <'container-type'> ]?

@container <name>? [ / <type> ]? { ... }

You'd get things like this:

container: name / type;
container: name name2 name3 / type type2;

@container name / type2 (query) { ... }
@container name (query) { ... }
@container / inline-size (query) { ... }

which doesn't seem unreasonable.

@mirisuzanne
Copy link
Contributor Author

I like this direction, giving container-name the primary slot — but we may want to combine that with a de-prioritizing of container-type. Right now we require a type in order to establish a container at all. I think, instead, we would want to say that every element(?) is a style container by default (maybe with a normal keyword as the initial value?), and then the type is only required when we want to query size? That maybe needs some more fleshing out, and could become a new issue, but I think it would help make this syntax work.

@mirisuzanne
Copy link
Contributor Author

mirisuzanne commented Dec 7, 2021

I think there are two or three potential consecutive resolutions here:

  1. Change the query preamble syntax to:
    @container <name>? [ / <type> ]? { ... }
  2. Change the container shorthand to match, with name first:
    container: <'container-name'>? [ / <'container-type'> ]?;
  3. Make container-type optional for establishing containers:
    • Containers with a name and no type are considered style containers
    • Or perhaps all elements are style containers by default?

@bramus
Copy link
Contributor

bramus commented Dec 8, 2021

Perhaps all elements are style containers by default

If that were the case, that would solve Higher Level Custom Properties that were talked about late 2020, no? If I interpret things correctly, the there proposed @if (<style-query) { … } could be seen as syntactic sugar for @container / style (<style-query) { … }, right?

Furthermore, if all elements were style containers by default, is style still a necessary value for container-type? Reasoning: as all elements already are style containers, there's not need to define them manually as such, right?
Note that in this case, as a side-effect to that decision, setting container-type to, for example, inline-size would be something to apply on top of it already being a style container. I.e. container-type: inline-size; will result in the element being both a style and size container.


Regarding the syntax itself: I feel that there's some tight clinging onto the @container at-rule here. With all elements (possibly) being style containers, it — as an author — would feel more logical to write @style(<style-query>) { … } instead of @container / style (<style-query) { … } though.

IIRC there is discussion somewhere that warned/decided against introducing a lot of new at-rules, so I can definitely understand. From an author's POV however, it feels a bit weird though. I think this "weirdness" stems from the fact that viewport media queries (@media) always are size based queries, not style based ones.

My initial reaction to container queries is the same: using @container is about the size of the element. I understand that it's more than size only, but I can imagine a lot of authors will be confused about it or only get part of the message (the size part), missing out on style/state/x containers.


What I'm trying to say here: as an author, this piece of code would be highly readable, even if I wouldn't know the exact details about style or container queries:

.item {
  --effect: none;
  …

  @style (--effect: fancy) { /* targets self */
    …
  }

  @container (width >= 20em) { /* targets closest parent with size containment */
    …
   }
}

A version that uses @container / style (…) instead would be harder to read and can confusing, as @container there targets a parent whereas @container / style targets the element itself.

Given this last confusion I'm raising here: yes, this could be seen as a plea for keeping @container reserved for only size based querying.


Apologies if I'm sidetracking here, or if I'm raising things in here that were already discussed. Feel free to ignore what's not relevant, I know you all will make the correct decisions in the end :)

(Congrats on hitting FPWD BTW! 🎉)

@mirisuzanne
Copy link
Contributor Author

Furthermore, if all elements were style containers by default, is style still a necessary value for container-type? … I.e. container-type: inline-size; will result in the element being both a style and size container.

That seems right to me.

it — as an author — would feel more logical to write @Style() { … } instead of @container / style (<style-query) { … } though.

Specifying the container-type isn't required in the current syntax, and (as you note) style might not even be needed as a type if we do make it a default everywhere. We also made the queries into functions. So the currently proposed syntax here for most cases would be:

@container style(--effect: fancy) {…}
@container size(width >= 20em) {…}

I think that's already pretty readable? What I like about @container as a rule for both size and style is that it clarifies what element you are querying, before stating what feature to query – and then it allows you to optionally filter those elements by a container name:

@container card style(--effect: fancy) {…}
@container article size(width >= 20em) {…}

If we say that every element is a style container, then there would be no reason to ever use / style in the preamble, since that would not filter out any elements.

My initial reaction to container queries is the same: using @container is about the size of the element. I understand that it's more than size only, but I can imagine a lot of authors will be confused about it or only get part of the message (the size part), missing out on style/state/x containers.

There is nothing about the word "container" that implies size, so this would only be true for authors with a strong historic attachment to "container queries" as an Idea. That seems to me like a very focused and temporary issue, easily overcome by some good resources.

@bramus
Copy link
Contributor

bramus commented Dec 9, 2021

Thanks for clarifying, Miriam.

I was a bit confused about the functions part because the examples in the ED don't use it. For example, I see:

@container card (inline-size > 30em) and (--responsive = true) { … }

The definition for <container-query> does already include this change

<container-query>     = ( <container-condition> )
                      | size( <size-query> )
                      | style( <style-query> )

So if I understand correctly, and if this change gets accepted, the snippet above would become this:

@container card size(inline-size > 30em) and style(--responsive = true) { … }

That's correct?

(As long as this issue is still open, I can see why the examples aren't updated yet. No worries)


There is nothing about the word "container" that implies size, so this would only be true for authors with a strong historic attachment to "container queries" as an Idea.

I know what I'll be explicitly mentioning whenever I get to talk/write about them :)

@mirisuzanne
Copy link
Contributor Author

Yes, that's correct. I'll make sure to update the examples at some point here.

I know what I'll be explicitly mentioning whenever I get to talk/write about them :)

haha, great!

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed CSS Conditional L3 : Syntax for container types, and agreed to the following:

  • RESOLVED: Boxes default to style containment, syntax of 'container' and @container preamble is name / type
The full IRC log of that discussion <fantasai> Topic: CSS Conditional L3 : Syntax for container types
<fantasai> github: https://github.com//issues/6393
<fantasai> miriam: Really wanted a way to query which type of conainer
<fantasai> miriam: e.g. find nearest inline-size container
<fantasai> miriam: Already the query has two parts to it
<fantasai> miriam: can query a specific named container
<fantasai> miriam: and then main query list is what queries against that container
<fantasai> miriam: so adding to preamble ability to filter by container type
<fantasai> miriam: in the discussion fantasai suggested that we could matched the syntax of the container shorthand property
<fantasai> miriam: currently that syntax requires a type, and then slash, and then any number of names
<fantasai> miriam: that could work, but in our initial formulation the container type is required to establish a container
<fantasai> miriam: which is why it's first
<fantasai> miriam: but for queries, seems that putting name first makes sense
<fantasai> miriam: since most ppl will be querying name, not type
<fantasai> miriam: Also discussion that all boxes can be style containers
<fantasai> miriam: which would make type container
<fantasai> miriam: make a style conainer
<fantasai> miriam: in that case, we could start with names in container property
<fantasai> miriam: and if you wanted to make a type of container without a name, would have to start the value with a slash
<fantasai> Rossen_: Any opinions?
<miriam> this comment lays it out: https://github.com//issues/6393#issuecomment-988216116
<fantasai> miriam: Comment summarizes the proposal
<fantasai> miriam: 3 resolutions
<fantasai> miriam: 1. Make style as the default container type, all boxes can take
<fantasai> miriam: 2. Changing 'container' property syntax to name / type
<fantasai> miriam: 3. Changing @container preamble to match
<fantasai> florian: I think we shoud take all 3, only make sense if we take them all
<fantasai> Rossen_: Any objections to resolving on all 3?
<fantasai> RESOLVED: Boxes default to style containment, syntax of 'container' and @container preamble is name / type
<fantasai> Rossen_: Do we need to republish soon?
<fantasai> miriam: need to make some changes first

@una
Copy link
Contributor

una commented Jan 13, 2022

I don't think we should default everything to being a style container. This came up with currentBackgroundColor

Consider the following:

<div class="card">
  <img />
  <div class="meta">
    <p>Text <a href="#">Link</a></p>
    <button>
      Buy Me on <span>Store</span>
      <span>$10.99</span>
    </button>
  </div>
</div>

If I wanted to change the color of the spans or<a>'s, based on the card background color, it would use transparent by default and not the .card's background color. In this case, we wouldn't want that — we'd want a higher-level parent and not a direct parent. We'd want to query the .card container.

Setting explicit containers works better in this case, and setting container-type will make it easier to have expected results. Maybe we should revert the decision to have implicit containers?

@mirisuzanne
Copy link
Contributor Author

While having all elements default to style containers can cause issues for non-inherited properties (like @una's background-color example), I think it works out great for inherited properties: checking the parent font size, or current font-style, weight, color, etc. Those use-cases are very handy for solving style-toggle use cases (if parent is italic, child should be normal, and vice versa).

For use-cases where it's important to query a specific container (including user-defined container types like 'containers with backgrounds'), setting a container-name makes it much more robust against the possibility of accidental intervening containers. Whether or not we revisit this resolution, I would still push authors towards naming containers when that's important — as it is with background-color.

I'm happy to revisit this if we want to put it back on the agenda (or discuss it as a part of resolving #6644), but at this point I still think we made a reasonable decision.

@una
Copy link
Contributor

una commented Feb 9, 2022

My main issue with this is I just don't think it's a reasonable default to assume that a user wants to query the style of a direct parent in a container query. In my current experience with CQ, the majority of my demos and use cases do not query the direct parent. To help illustrate:

CQ Card

  • adjusting styles of .desc which is within a block called .meta but I'm querying the parent .card's container size

Screen Shot 2022-02-09 at 10 24 19 AM

in:

Screen Shot 2022-02-09 at 10 28 50 AM

Button Component Inside CQ Card

  • The span within .btn-text is querying button
  • button within div within .card is querying card-container
  • The only thing directly querying its parent is plus-icon within .cart-icon because it happens to be organized this way

Screen Shot 2022-02-09 at 10 30 38 AM

in
Screen Shot 2022-02-09 at 10 38 04 AM

@astearns
Copy link
Member

astearns commented Feb 9, 2022

In #6644 (comment), we resolved to remove type from the preamble

  • RESOLVED: remove the container-type syntax from the preamble of the @container rule

@mirisuzanne
Copy link
Contributor Author

I split out the remaining questions here into a new issue #7066. Closing this issue as resolved, as we've decided to remove container-type selection syntax, and rely on automated container-type selection.

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

8 participants