[go: nahoru, domu]

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] CQ vs shadow boundaries #5984

Open
tabatkins opened this issue Feb 12, 2021 · 49 comments
Open

[css-contain] CQ vs shadow boundaries #5984

tabatkins opened this issue Feb 12, 2021 · 49 comments

Comments

@tabatkins
Copy link
Member

Can a CQ see past shadow boundaries? If so, exactly how?

If the CQ is done via selectors, the answer is obvious - you can only CQ against elements you can see via selectors. So from within a shadow, the highest ancestor you can CQ against is your host element, no higher.

If CQ is done via an at-rule, the answer is not quite as clear; theoretically, we can walk the box tree (flat tree) to find ancestors. However, we do not want a ::part() to be able to see an ancestor within the targeted shadow (it violates shadow encapsulation). And letting a shadow see ancestors outside in the light DOM gives us similar power to :host-context(), which already makes browser vendors unhappy, so maybe we don't want that either. And that's all the cases, so presumably we don't want it to work in either case.


As a related issue, we'll need to specifically define that the CQ pseudo-class matches on host elements, since they're featureless and don't match anything besides :host by default otherwise.

@SebastianZ
Copy link
Contributor

And with CQ you probably mean container queries, right?

Sebastian

@svgeesus
Copy link
Contributor
svgeesus commented Feb 12, 2021

probably

Yes. (Cadit quaestio)

@mirisuzanne
Copy link
Contributor

To clarify, neither approach uses a selector to target the container. Both of these would select a .card for conditional styling, and the container remains implicit/contextual:

@container (width > 40em) { .card { ... } }
.card:container(width > 40em) { ... }

Maybe they still have different shadow-DOM implications under the hood?

@lilles
Copy link
Member
lilles commented Jun 8, 2021

For the implicit case, it follows from the layout tree, but what if you have a web component where you want to have your styles depend on the closest container and there is no such container inside the component. Do you really want to query a container outside of your component? Would it be natural that the shadow host is the outermost container to query and that the CQ fallback happens on the shadow host level?

For the container-name proposal it's a question of name clashes. If you have a @container mycontainer (inline-size >= 200px), you probably don't want to match an arbitrary mycontainer outside of your web component when you don't have a mycontainer in your ancestor chain inside the component?

@mirisuzanne
Copy link
Contributor

I think I would often want the ability to query the host page - this feature is all about access to contextual information - but that the host element might suffice in most situations (especially for dimensional queries), if we need that limitation.

If I was building a component library with shadow-DOM, I would want content-based components to query external layout containers - and I would likely try to do that with containers named by type (e.g. @container component (…) and @container layout (…)).

@bkardell
Copy link
Contributor

I think a bunch of people are playing with container queries, have we collected any feedback on specifically this from others? It seems like it would be very valuable... Has someone asked for either?

@lilles
Copy link
Member
lilles commented Nov 1, 2021

The current implementation in Blink uses the flat tree for looking up containers. That was the most straightforward way to implement it. I agree that ::part() and pseudo elements like ::placeholder are problematic if they match containers inside the shadow tree.

This issue should probably be considered together with issue #6711.

@lilles
Copy link
Member
lilles commented Nov 1, 2021

I think this is an interesting case. Two selectors for the same element in different scopes querying the closest container for inline-size:

<!doctype html>
<style>
  host-element, host-child {
    container-type: inline-size;
  }
  @container (width <= 300px) {
    host-child {}
  }
</style>
<host-element>
  <template shadowroot="open">
    <style>
      inner-container { container-type: inline-size; }
      @container (width <= 200px) {
        ::slotted(host-child) {}
      }
    </style>
    <inner-container>
      <slot></slot>
    </inner-container>
  </template>
  <host-child>Light content</host-child>
</host-element>

Do we want host-child to have host-element as its container when the query/selector is in the host-child tree, and have inner-container as the container for the ::slotted rule?

If we just look for the container in the element's own tree, we would not be able to style slotted elements inside the shadow tree. Is that acceptable?

@lilles
Copy link
Member
lilles commented Nov 8, 2021

With container-name it gets even more interesting.

Should container-name be tree-scoped names?

In the example below, using tree-scoped names would mean that the container rules for host-child below would match differerent containers based on the scope of the rule:

<!doctype html>
<style>
  host-element {
    container-type: inline-size;
    container-name: a;
  }
  @container a (width <= 300px) {
    host-child {}
  }
</style>
<host-element>
  <template shadowroot="open">
    <style>
      inner-container {
        container-type: inline-size;
        container-name: a;
      }
      @container a (width <= 200px) {
        ::slotted(host-child) {}
      }
    </style>
    <inner-container>
      <slot></slot>
    </inner-container>
  </template>
  <host-child>Light content</host-child>
</host-element>

@tabatkins
Copy link
Member Author

Container names don't need to be tree-scoped; they're not globally registered/visible, but are instead solely looked up via an ancestor search.

We could potentially still censor names past a shadow boundary if we wanted, but that would be an independent decision; it's not forced by this issue.

@mirisuzanne
Copy link
Contributor

I like the idea of allowing slotted elements to be styled based on the slot location in the shadow tree. It seems likely to me that both styles from inside and outside the shadow boundary are attempting to determine actual space available, and in most cases the shadow parent will give the better answer - but that may not be true in all cases.

@lilles
Copy link
Member
lilles commented Nov 11, 2021

@tabatkins By ancestor search, do you mean looking up ancestors in the light tree without walking past shadow roots?

If so, it means host-child will never match any containers inside host's shadow tree:

<style>
  @container name (min-width: 100px) { host-child {} }
  @container (min-width: 100px) { host-child {} }
</style>
<host-element><host-child></host-child></host-element>

What about slotted rules inside a shadow tree:

<style>
  @container name (min-width: 100px) { ::slotted(div) {} }
  @container (min-width: 100px) { ::slotted(div) {} }
</style>
<div>
  <slot></slot>
</div>

Do we start walking from the slot instead of the slotted element to find the container so that it's possible to match containers inside the shadow tree?

@lilles
Copy link
Member
lilles commented Nov 11, 2021

The current spec covers first case if "descendants" are light tree descendants.

If ::slotted rules should match containers walking from the slot instead of the slotted element being matched, the spec needs to say something about that.

@andruud
Copy link
Member
andruud commented Nov 11, 2021

However, we do not want a ::part() to be able to see an ancestor within the targeted shadow (it violates shadow encapsulation).

Possibly stupid question: why is this worse than e.g. host::part(thing) { background-color: var(--defined-in-shadow-tree); }?

@tabatkins
Copy link
Member Author

By ancestor search, do you mean looking up ancestors in the light tree without walking past shadow roots?

No, I was referring to jumping up shadow roots as well; CSS almost always operates on the flat tree and doesn't care about shadow boundaries, particularly when layout is involved.

That said, while shadows being able to see ancestor light containers seems reasonable, i agree that parts being able to see shadow containers isn't good.

Possibly stupid question: why is this worse than e.g. host::part(thing) { background-color: var(--defined-in-shadow-tree); }?

Name collisions among variables, while possible, aren't too likely in general. On the other hand, the common use-case for CQs doesn't even refer to the container by name, so you'll often end up accidentally being intercepted by the shadow's container, when you intended to be querying off of some light-dom container that's an ancestor to the shadow.

@andruud
Copy link
Member
andruud commented Nov 11, 2021

OK, so what about specifying that container selection can't see containers in tree scopes below the scope where the @container rule is defined? Basically always flat tree, but skips containers that are below the "originating tree scope".

@lilles
Copy link
Member
lilles commented Nov 11, 2021

OK, so what about specifying that container selection can't see containers in tree scopes below the scope where the @container rule is defined? Basically always flat tree, but skips containers that are below the "originating tree scope".

That's per definition shadow-including descendants/ancestors

@tabatkins
Copy link
Member Author

That's per definition shadow-including descendants/ancestors

No, that's basically flat tree - a ::part(), selecting an element in the shadow, would see shadow ancestors before it escapes back into the light tree and starts seeing light ancestors.

Anders is talking about something more akin to the tree-scoped names rule, where the tree scope of the @container rule affects what container elements are visible to it. A @container { ::part() {...} }, since it appears in the light tree's styles, will skip container elements automatically until it's back in the tree scope it was defined in, at which point it can see all ancestor scopes as well.

I think that makes sense?

@lilles
Copy link
Member
lilles commented Nov 15, 2021

I meant that the part about: "... but skips containers that are below the "originating tree scope" ..." is about shadow-including descendants/ancestors.

@andruud
Copy link
Member
andruud commented Nov 16, 2021

(After decoding cryptic DOM spec language):

Shadow-including ancestors is almost what we're talking about, but we need to skip the "originating tree scope" based on where the container rule comes from, not based on where the element is in the tree. Otherwise ::slotted will not work as expected.

@lilles
Copy link
Member
lilles commented Nov 27, 2021

After an off-line discussion with @andruud we think the discussed behavior could be specified as:

In https://drafts.csswg.org/css-contain-3/#query-container, change:

"... styling its descendants ..."

to:

"... styling its shadow-including descendants ..."

And add:

"For selectors with pseudo elements, query containers can be established in the shadow-including inclusive ancestors of the originating element."

Resolving on this could also resolve issue #6711.

  1. ::slotted() selectors can query containers inside the shadow tree (including the slot itself)
  2. ::part() selectors can query the host, but not internal query containers inside the shadow tree
  3. ::placeholder and ::file-selector-button can query the input element, but not any internals if the input element is implemented using Shadow DOM
  4. ::before, ::after, ::marker, and ::backdrop queries its originating element
  5. ::first-letter and ::first-line queries its originating element, even if the fictional elements may be pushed down past other elements establishing query containers for the purpose of inheritance and rendering.
  6. Multiple pseudo elements do not allow pseudo elements to be query containers for other pseudo elements. E.g., the host, but not the part() can be the query container for ::before in: host::part()::before. Similarly, the ::before element can not be the query container for the ::marker in: div::before::marker.

I have uploaded a Chromium CL with supporting tests for the Shadow DOM cases here:

https://chromium-review.googlesource.com/c/chromium/src/+/3304155

@andruud
Copy link
Member
andruud commented Nov 29, 2021

👍

But regarding:

"For selectors with pseudo elements, query containers can be established in the shadow-including inclusive ancestors of the originating element."

we need to describe how to look for containers without mentioning the "kind" of selector.

For e.g. div::before::marker, is there a definition which refers to div from the perspective of both ::before and ::marker? If not, call this "originating element root" or something. Then specify that we look for containers in the shadow-including inclusive ancestors starting from the "originating element root".

@lilles
Copy link
Member
lilles commented Nov 29, 2021

👍

But regarding:

"For selectors with pseudo elements, query containers can be established in the shadow-including inclusive ancestors of the originating element."

we need to describe how to look for containers without mentioning the "kind" of selector.

For e.g. div::before::marker, is there a definition which refers to div from the perspective of both ::before and ::marker? If not, call this "originating element root" or something. Then specify that we look for containers in the shadow-including ancestors starting from the "originating element root".

I don't think it looks like the spec is written with multiple pseudo elements in mind:

https://drafts.csswg.org/selectors-4/#pseudo-element-attachment

As you say, we either need to clarify that div is the originating element for ::marker in div::before::marker, or introduce something like "root originating element", yes.

@lilles
Copy link
Member
lilles commented Jan 18, 2022

The implementation behind a flag for container queries in Blink now implements the behavior proposed in #5984 (comment)

Tentative tests landed here: https://wpt.live/css/css-contain/container-queries/container-for-shadow-dom.tentative.html

See #6711 (comment) for tests for pseudo elements.

@lilles lilles added the Agenda+ label Jan 18, 2022
@tabatkins
Copy link
Member Author

Excellent! And I agree that the proposed behavior (use shadow-inclusive descendants/ancestors, with pseudos jumping straight to their originating element) sounds good; it uses only information available at selector-evaluation time, and respects tree scopes reasonably.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-contain] CQ vs shadow boundaries, and agreed to the following:

  • RESOLVED: Accept proposal to in all cases use originating element
The full IRC log of that discussion <dael> Topic: [css-contain] CQ vs shadow boundaries
<dael> github: https://github.com//issues/5984
<dael> RESOLVED: Accept proposal to in all cases use originating element

@astearns
Copy link
Member
astearns commented Mar 30, 2022

Hand-edited minutes, as the bot dropped before it could post:

RESOLVED: No change, work on more general solution to more openness across shadow boundaries

 Topic: CQ and Shadow Boundaries
 github: https://github.com/w3c/csswg-drafts/issues/5984
TabAtkins: Miriam added to agenda
miriam: Issue is, we resolved awhile back that slotted container queries couldn't see containers in the shadow DOM
miriam: which from the perspective of saying that Shadow DOM things should be hidden makes a lot of sense
miriam: Problem is very common use case with web components and shadow DOM these days is to build an entire design system out of them
miriam: you end up with lots of things nested in slots
miriam: and if container queries can't respond, feels limiting
miriam: decent workaround, similar to can't measure grid track with CQ, add an extra element in the slot and measure that
miriam: but wanted to bring that back because feels broken to ppl using the use case
miriam: Proposed path forward is that Container Queries can respond to containers that are in the shadow DOM
miriam: that does expose a bit about what's in the shadow DOM
miriam: but feels similar to exposing inheritance
miriam: we're trying to measure space available, and that's impacted by what's in the shadow DOM
miriam: I can also seem Tab's perspective, there's a disconnect here in the various ways shadow DOM is used
TabAtkins: My position is that containers declared inside a shadow shouldn't be visible to light DOm chilidren distributed in slots
TabAtkins: Shadow DOM was designed to be encapsualted as much as possible
TabAtkins: some purposeful leaks, e.g. ::parts
TabAtkins: and inheritance leaks through the boundaries, but is minor
TabAtkins: but a number of other things we didn't want things to leak
TabAtkins: e.e.g font-faces in shadow can't be used by light DOM
TabAtkins: ...
TabAtkins: if you ever say anything in your styles about that font, won't see that font
TabAtkins: because loaded in Shadow only
TabAtkins: this feels similar
TabAtkins: container in the shadow is not necessarily part of the public contract of the shadow implementation
TabAtkins: might be internal, not to be exposed
TabAtkins: if exposed, then existance of that container *is* exposed
TabAtkins: and becomes part of your contract
TabAtkins: That said, I can see the useful ness of having containers in your shadow, and making that part of your "API" with surrounding content
TabAtkins: This is showing a conflict between shadow DOM as hidden ?? and shadow DOM as organizing mechanism
TabAtkins: ...
TabAtkins: allows cooperating first-party components
TabAtkins: I think we should have a more open shadow DOM that doesn't have info-hiding, and those could expose things like containers and name-defining @rules
TabAtkins: but I don't think that should be an exception to shadow DOM as we have it today
TabAtkins: we should continue to info-hide when possible
TabAtkins: doing this would make it impossible for any UA shadow DOM usage to use containers
TabAtkins: and that's problematic
TabAtkins: so I thin we should keep spec as is, not exposed
miriam: Container in Shadow DOM does impact layout, so that's why it feels weird for it to not impact the measurement
miriam: but I do like the idea of having some explicit control here
TabAtkins: I do get it. Not a good answer right now.
TabAtkins: My preferred decision isn't ideal, it's best given the current tack.
TabAtkins: and we should solve the problem more generally
Rossen_: My current personal lean is more towards trying to find more general solution, that will work for this and others
Rossen_: and not rush into open more dependencies and make shadow DOM less shadowy
 +1
Rossen_: recognizing the pain as well
Rossen_: Are we in a position to break that tie?
Rossen_: Miriam, how do we feel about this?
miriam: As long as we all feel the pain, and want to work towards a general solution
Rossen_: Miriam, if we resolve to work on more general solution, would it be objectionable?
miriam: No, I would want to work on the more general solution, and teach the workaround for now
Rossen_: sounds like consensus to me, anyone else with a different opinion?
Rossen_: OK, proposed solution is to leave spec as-is and work on a more generalized solution to less shadowy shadow boundaries
Rossen_: any objections?
 RESOLVED: No change, work on more general solution to more openness across shadow boundaries

@emilio
Copy link
Collaborator
emilio commented Feb 28, 2024

Reopening as per the discussion in #7947. IMO for style queries it seems you really really want the flat tree.

@emilio emilio reopened this Feb 28, 2024
@emilio emilio added the Agenda+ label Feb 28, 2024
@mirisuzanne mirisuzanne moved this from Done to In progress in Container Queries [css-contain] Feb 28, 2024
@mirisuzanne
Copy link
Contributor

I think this is true for basically all queries: the goal is to measure your nearest context. But style queries and query units in particular expose the ways a container query is more akin to 'inheritance' than selection. The shadow DOM has always provided context for slotted elements, and containers are explicitly designed for providing that sort of context. If we need to make container names private to a shadow tree, or allow container names to be marked as private, that feels like a separate issue to me.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-contain] CQ vs shadow boundaries, and agreed to the following:

  • RESOLVED: Container queries and units use the flat tree
The full IRC log of that discussion <flackr> emilio: the current behavior of cq and cq units is weird for authors, especially the units. For stuff like style queries, current behavior is using the regular dom which kind of makes sense. But for style queries you really want the flat tree
<miriam> q+
<TabAtkins> q+
<flackr> emilio: we opened this as a result of another issue filed on the units which are right now defined to behave like the queries. The original resolution isn't the best for authors, I think it's a bit weird. miriam thinks so as well and maybe this is worth reverting.
<flackr> emilio: firefox implements what i'm proposing and we haven't run into any issues so i think the compat risk is small
<astearns> ack miriam
<flackr> miriam: I've felt strongly from the start that we went the wrong way. CQ are very much akin to inheritance in a lot of ways and should be treated in that way rather than as selectors in terms of relation to shadow dom. They're all about context and shadow elements and slotting creates context which should be able to be accessed
<astearns> ack TabAtkins
<kizu> q+
<flackr> TabAtkins: ultimately I agree [missed]
<astearns> ack kizu
<flackr> kizu: may be worth mentioning named CQ where we cannot assess a named entity from outside of the shadow dom
<flackr> astearns: any other comments / concerns?
<flackr> emilio: Would be interested in hearing rune's thoughts, can weigh in on issue
<flackr> Proposed resolution: Container queries and units use the flat tree
<flackr> RESOLVED: Container queries and units use the flat tree
<emilio> scribenick: emilio

@mirisuzanne mirisuzanne moved this from In progress to Needs Edits in Container Queries [css-contain] May 15, 2024
moz-wptsync-bot pushed a commit to web-platform-tests/wpt that referenced this issue Jun 3, 2024
As per CSSWG resolution in:

  w3c/csswg-drafts#5984 (comment)

Differential Revision: https://phabricator.services.mozilla.com/D212412

bugzilla-url: https://bugzilla.mozilla.org/show_bug.cgi?id=1790886
gecko-commit: bc97304e5b3d4d2110fd8f2120ba9914406a116a
gecko-reviewers: jwatt
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue Jun 4, 2024
moz-wptsync-bot pushed a commit to web-platform-tests/wpt that referenced this issue Jun 4, 2024
As per CSSWG resolution in:

  w3c/csswg-drafts#5984 (comment)

Differential Revision: https://phabricator.services.mozilla.com/D212412

bugzilla-url: https://bugzilla.mozilla.org/show_bug.cgi?id=1790886
gecko-commit: bc97304e5b3d4d2110fd8f2120ba9914406a116a
gecko-reviewers: jwatt
ErichDonGubler pushed a commit to ErichDonGubler/firefox that referenced this issue Jun 5, 2024
i3roly pushed a commit to i3roly/firefox-dynasty that referenced this issue Jun 14, 2024
gordonbrander added a commit to commontoolsinc/labs that referenced this issue Jun 20, 2024
...Unfortunately they do not work across slot boundaries. The container
unit is sized by the light dom container, not the shadow dom container.

See w3c/csswg-drafts#5984

Solution: use percentage units instead. Oddly enough, these do work
across shadow boundaries.
gordonbrander added a commit to commontoolsinc/labs that referenced this issue Jun 20, 2024
* Add exports

* Stop using container units
...Unfortunately they do not work across slot boundaries. The container
unit is sized by the light dom container, not the shadow dom container.

See w3c/csswg-drafts#5984

Solution: use percentage units instead. Oddly enough, these do work
across shadow boundaries.

* Update all classes to use Common prefix
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests