w3c / csswg-drafts

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

[css-shadow-parts] Make `::slotted()` a combinator #7922

Open LeaVerou opened 1 year ago

LeaVerou commented 1 year ago

Currently ::slotted() is a functional pseudo-element, which causes no end of problems:

Making it a pseudo-class is not an option, as discussed in https://github.com/w3c/csswg-drafts/issues/7305 . However, unless @fantasai and I are missing something, it should be possible to make it a combinator (/slotted/), which would address all of the above.

::part() has similar issues, but is not equally trivial to turn into a combinator because that would give the outside world access to too much of the shadow subtree, but ::slotted() refers to the light DOM so doesn't have these problems.

Westbrook commented 1 year ago

All of those targeting options sound AMAZING! 😍

When you say "make it a combinator", what does that really mean? Is that a syntax change? Is that an interpreter change? Something else?

If this is an interpreter change, is there room to as part of this investigation to look into ways that we might alter the specificity of a ::slotted() selector? By default, it is very low, which leads to usage of the !imporant flag when attempting to "require" certain visual delivery of slotted content. Being able to manage this more explicitly via the selector in some way would prevent needing to use !imporant across a myriad of rules.

If this allows for targeting descendants, is there any benefit to investigating the inclusion of :host in this conversation as well? It also refers to the light DOM, and while we've recently seen Safari add support for the :has() selector to it, there are many other concepts that could be productive added to it (e.g. :host-context equivalent selection) that would be looked on quite favorably by the community.

One other ::slotted()-centric question that I've seen recently is the quest for :has(::slotted(*)). Any change these two ideas vibe together?

LeaVerou commented 1 year ago

All of those targeting options sound AMAZING! 😍

When you say "make it a combinator", what does that really mean? Is that a syntax change? Is that an interpreter change? Something else?

Combinators are things like >, +, ~ etc. They are used to target elements based on their relationships.

If this is an interpreter change, is there room to as part of this investigation to look into ways that we might alter the specificity of a ::slotted() selector? By default, it is very low, which leads to usage of the !imporant flag when attempting to "require" certain visual delivery of slotted content. Being able to manage this more explicitly via the selector in some way would prevent needing to use !imporant across a myriad of rules.

The problem is not that its specificity is low, but that it operates on an entirely different level of the cascade. @mirisuzanne, @fantasai and I were discussing how to address this just the other day, but it's orthogonal to this. If you have any pointers to discussions about this problem (besides the issue I linked to above) they would be useful.

If this allows for targeting descendants, is there any benefit to investigating the inclusion of :host in this conversation as well? It also refers to the light DOM, and while we've recently seen Safari add support for the :has() selector to it, there are many other concepts that could be productive added to it (e.g. :host-context equivalent selection) that would be looked on quite favorably by the community.

This is also a separate issue, but any pointers you may have are useful, so we can open a new issue to discuss it!

PS: I merged your two consecutive comments, hence the deleted comment notice below 😊

EisenbergEffect commented 1 year ago

Yes, please. A thousands times yes.

Westbrook commented 1 year ago

Sorry for not being more clear here:

Combinators are things like >, +, ~ etc. They are used to target elements based on their relationships.

Does that mean we would alter the interpretation of ::slotted() or pair it with something that altered the syntax of :host ::slotted(input) to (pretend use %) :host % input? I'm looking to see what you're thinking regarding the size of the delta here: plug-n-play, additive, breaking, etc.

I'll hunt down related issues/content on those other points, as I know they've been brought up in other contexts, I just can find them just this minute.

bennypowers commented 1 year ago

This would solve so many problems our design systems team has come across. I could list 5 reports just from the last week!

LeaVerou commented 1 year ago

Does that mean we would alter the interpretation of ::slotted() or pair it with something that altered the syntax of :host ::slotted(input) to (pretend use %) :host % input? I'm looking to see what you're thinking regarding the size of the delta here: plug-n-play, additive, breaking, etc.

Not sure I fully understand your question, but we can certainly not remove ::slotted() (yet) or alter its interpretation as that would cause compat issues, this would be an addition. If once the combinator is well established, ::slotted() usage drops below a certain theshold, maybe we could remove it then.

Btw no need to use a placeholder symbol, for something like this I don't think more ascii art is appropriate, something like /slotted/ is fine.

One small downside of a combinator is that you need to target the element the combinator operates on, i.e. the slot element, which makes for somewhat awkward syntax e.g. slot /slotted/ input or just * /slotted/ input instead of ::slotted(input).

This would solve so many problems our design systems team has come across. I could list 5 reports just from the last week!

Please do!

bennypowers commented 1 year ago

A major issue we're running into is lightdom (i.e. app-level) css interfering with our component styles.

There are some cases where we can ::slotted(thing) { what: ever !important; } but we can't at all do that with nested content. So we have to choose between flexible components or effective distribution, a choice which is liable to produce neither outcome.

We're offering workarounds with our lightdom stylesheets, but we'd prefer not to have those at all, and just do everything in shadow DOM, which would be easier to use, more performant, and more maintainable.

Maybe @heyMP would like to elaborate

LeaVerou commented 1 year ago

A major issue we're running into is lightdom (i.e. app-level) css interfering with our component styles.

I see. So, making ::slotted() a combinator wouldn't directly help with that (the problem is that shadow DOM styles operate on a different level of the cascade), though it would make it easier to address it, since with this approach at least you're targeting the element in question directly, and not through another element. I do plan to file a separate issue for this, so more info and use cases is certainly welcome.

bennypowers commented 1 year ago

yes, exactly. it would remove the need for more-error-prone lightdom stylesheets, and given a later proposal to somehow bump the priority of ::slotted, would allow us to write-once-run-everywhere at greater scale and pace.

heyMP commented 1 year ago

As mentioned above, the low specificity of ::slotted() causes us a lot of pain. Any CSS reset file wipes out our styles which forces us to use !important everywhere.

Other than that, the ability to have complex selectors will go a long way. We could eliminate the requirement of having to load a lightdom.css file just to target elements in an unordered list. 😀

romainmenke commented 1 year ago

Is there overlap with https://github.com/w3c/csswg-drafts/issues/7346 ?

LeaVerou commented 1 year ago

Is there overlap with #7346 ?

Yes, but the ability to add combinators after ::slotted() would only solve some of the issues with it, not all.

heyMP commented 1 year ago

This is probably out of scope for this ::slotted() discussion but I'll give this issue some visibility also just in case. Keyframe animations defined in the parent shadow roots aren't available for ::slotted() children.

This animatein keyframe won't work.

:host([animate]) ::slotted(*) {
   animation-name: animateIn;
}

@keyframes animateIn {
  0% {
    opacity: 0;
    transform: translate(0, -100%);
  }

  100% {
    opacity: 1;
  }
}

https://github.com/Polymer/polymer/issues/4688

castastrophe commented 1 year ago

@heyMP Very interesting! I hadn't considered a use-case like this yet but it makes perfect sense. Keyframes are CSSRules and so it's being defined inside a different tree than the light DOM content.

castastrophe commented 1 year ago

As mentioned above, the low specificity of ::slotted() causes us a lot of pain. Any CSS reset file wipes out our styles which forces us to use !important everywhere.

@heyMP Regarding this point, I have definitely felt this pain in the past though on other projects, I quite like and embrace that low specificity. Makes us evaluate what kind of light/shadow DOM support a component (or library) wants to enforce.

castastrophe commented 1 year ago

@heyMP @bennypowers Did I set up this demo on par with what you found in your keyframes example? https://codepen.io/castastrophe/pen/rNKjLVj?editors=0010

castastrophe commented 1 year ago

I'm finding that the keyframe defined inside the shadow DOM applies to ::slotted(*) and to the slot itself just fine.

Dove deep into this last night. Here's the summary of our findings:

Keyframe defined in the shadow DOM scope:

Keyframe defined in a style tag assigned to the slot:

Found no examples of a keyframe defined in the global DOM scope crossing the boundary into the shadow DOM.

With that, I'll stop squirreling on this topic as it feels unrelated to the primary goal laid out in the description.

LeaVerou commented 1 year ago

With that, I'll stop squirreling on this topic as it feels unrelated to the primary goal laid out in the description.

Can you open a new one though? This is an important topic that deserves separate consideration.

css-meeting-bot commented 1 year ago

The CSS Working Group just discussed [css-shadow-parts] Make `::slotted()` a combinator.

The full IRC log of that discussion <TabAtkins> lea: ::slotted() is a pseudo-element
<TabAtkins> lea: This doesn't really solve author's problems tho
<TabAtkins> lea: Four issue in the issue
<TabAtkins> lea: Can't use in querySelector
<TabAtkins> lea: Can't target children of slotted elements
<emilio> q+
<TabAtkins> lea: etc, lots because it's just a pseudo-element
<TabAtkins> lea: The WC community has chimed in and seem to think making this a combinator would solve these
<TabAtkins> lea: ::part() might have a similar approach but is more complicated to avoid exposing too much info, but ::slotted() refers to things in the light dom so it's not exposing anything too bad
<TabAtkins> lea: Discussing with fantasai, we don't see any issues spec-wise, but want to hear from impls
<castastrophe> q+
<TabAtkins> emilio: ::slotted() can also match in the shadow dom, it matches fallback content per spec (but unsure how well that's implemented)
<TabAtkins> emilio: Also as-is ::slotted() depends on which shadow tree you're in
<Rossen_> ack emilio
<TabAtkins> emilio: Also I think we do support ::slotted()::before, etc
<TabAtkins> emilio: probably not non-tree-abiding pseudos
<TabAtkins> emilio: If we don't support it, we should
<TabAtkins> castastrophe: My one concern with adding complexity to ::slotted(), there is already some confusion as to how it's applied
<TabAtkins> castastrophe: I have a codepen, like :first-child is first child in the DOM, not based on first in the slot
<lea> castastrophe: would love a link to the codepen!
<Rossen_> https://codepen.io/castastrophe/pen/rNKjLVj?editors=0010
<TabAtkins> castastrophe: So would be useful to clarify how things resolve relative to slot ordering, etc
<Rossen_> ack castastrophe
<fantasai> TabAtkins: I feel like that might be the reason we ended up designing ::slotted() the way it is, because you *can't* have sibling relationships
<fantasai> TabAtkins: unless it's a little more clear what context it's evaluated it
<fantasai> TabAtkins: but the rest of the issues lea brings up seem to be accidental damage
<lea> q+
<fantasai> TabAtkins: Not being able to descend into slotted element, no reason not to allow that
<castastrophe> Referenced codepen: https://codepen.io/castastrophe/pen/yLXpagw
<fantasai> lea: The point emilio brought up is good, that sometimes these elements can also be in the shadow DOM
<Rossen_> ack lea
<fantasai> lea: what if the slotted combinator didn't match such fallback content, only the author-provided elements
<fantasai> lea: but not sure if it satisfies the use cases, something to thin about
<TabAtkins> lea: I wonder if /slotted/ not matching fallback content would make sense. Not sure if it would still satisfy use-cases tho
<fantasai> TabAtkins: If you did want to distinguish other proposal to haveing a pseudo-class for whether using fallback content or not
<fantasai> TabAtkins: :has-slotted or something?
<TabAtkins> castastrophe: I think named slots is a bit of a challenge, what does light dom look like in multiple named slots
<lea> I think you can style fallback content with regular shadow DOM CSS, so it doesn't seem to be much of a problem at first glance?
<TabAtkins> castastrophe: The codepen does show the confusion about the elements having a relationship in the light dom vs in the slots
<lea> q?
<lea> q+
<Rossen_> ack lea
<TabAtkins> lea: I think you can style fallback content with regular CSS in the shadow.
<fantasai> TabAtkins: Emilio, you said that the ::slotted selector can match differently dependingon which shadow tree it's coming from, what did you mean?
<castastrophe> q+
<TabAtkins> fantasai: So frist thing to tink about is, if we add this, would we use the slotted relationship or the light dom relationship
<Rossen_> ack fantasai
<TabAtkins> fantasai: I think using the slotted relationship would be more understandable but not sure
<fantasai> lea: explain?
<fantasai> TabAtkins: Say you're slotting all the even elements
<fantasai> TabAtkins: and you asked :nth-child(2)
<fantasai> TabAtkins: is this the 2nd slotted item, or the 2nd item in the original DOM?
<fantasai> lea: we'd need to figure out what the author expectations are
<emilio> q+
<fantasai> TabAtkins: I expect there's complexity in evaluating on the slotted list
<TabAtkins> lea: Intuitively to me, as an author, it seems like the light dom relationship makes more sense
<fantasai> lea: intuitively as an author, it seems to me the light DOM relationships makes more sense
<fantasai> TabAtkins: Wanting to style first/last items in the slot using :first-child/:last-child is reasonable case
<lea> q+
<Rossen_> ack castastrophe
<fantasai> castastrophe: There's a lot of confusion over named slots
<fantasai> castastrophe: e.g. if you pull things into named slots, currently :first-child is only if it's the first child in the light DOM
<fantasai> castastrophe: but not if it's the first child in the slot
<fantasai> castastrophe: I think we'd need to be very clear in the spec
<fantasai> scribnick: fantasai
<fantasai> scribenick: fantasai
<Rossen_> ack emilio
<fantasai> emilio: Regarding what we talked before, matching where the selector is
<fantasai> emilio: in the case of nested slots, it woudl jump across to slots in the current shadow tree
<fantasai> emilio: that's the behavior I was talkinga bout before
<fantasai> castastrophe: if you have a slot that's passing content to a shadow template that also has.. if you have multiple?
<fantasai> emilio: from outer tree, slotted pseudo would cross that nested slot
<fantasai> emilio: the outer shadow tree
<fantasai> emilio: so you could still style the slotted contents ...
<fantasai> emilio: I need a whiteboard to reason about nested slots!
<fantasai> emilio: but I'm sure we jump across to the right scope
<fantasai> emilio: that scope is dependent on the tree that we're looking at right now
<fantasai> Rossen_: I'm hoping that made sense to you, Tab?
<castastrophe> q+
<fantasai> TabAtkins: I don't know that's necessarily right...
<fantasai> TabAtkins: I'd have to look up again the slot assignment algo
<fantasai> TabAtkins: I forget, if you're in nested slot, if we match the slot element from higher up or the things slotted in that element
<TabAtkins> Oh, it's after flattening
<Rossen_> ack fantasai
<TabAtkins> the elements can def come from multiple trees tho
<TabAtkins> q+
<fantasai> fantasai: I think we definitely need ability to style based on the slotted relationships
<fantasai> fantasai: as brought up before
<fantasai> fantasai: if you're pulling a subset of items into a slot, e.g. into a list
<Rossen_> Zakim, close queue
<Zakim> ok, Rossen_, the speaker queue is closed
<fantasai> florian: and you want to style every other item with :nth-child(), you want to get every other slotted item
<fantasai> s/florian/fantasai/
<fantasai> fantasai: not a random set of items based on what was selected or not from the light DOM
<fantasai> fantasai: similarly, as was brought up earlier for first/last child styling
<bkardell_> hmm
<TabAtkins> fantasai: We might also want the original light dom relationship
<TabAtkins> fantasai: But definitely think we need based on the slotted relationship
<bkardell_> Does that not imply that you know something about the dom inside the shadow root then?
<TabAtkins> fantasai: Since ::slotted() currently works based on light dom, one possibility would have ::slotted stay with that, but /slotted/ shift into "the slotted view of the world"
<TabAtkins> fantasai: we've discussed different ways to shift your view of the world
<bkardell_> Rossen_: yeah?
<fantasai> s/world/world, one of which was using subtrees under pseudo-elements
<fantasai> lea: there are two relationships, one based on relationships in light dom or slotted tree
<fantasai> lea: is one ??? the other?
<TabAtkins> lea: There's these two behavior - based on light dom, based on slotting - is one much easier to implement?
<fantasai> s/???/easier to implement than/
<fantasai> Rossen_: Does anyone have an answer?
<fantasai> emilio: slotted thing is new thing, so you need to somehow decide on ?? child matching vs
<fantasai> emilio: other thing is storage of slotted children different
<fantasai> Rossen_: sounds like light DOM is easier
<bkardell_> q+
<fantasai> emilio: but probably not impossible to make slotted work
<Rossen_> ack lea
<Rossen_> ack castastrophe
<fantasai> castastrophe: I agree, I think light DOM would be less complex
<fantasai> castastrophe: because that relationship is flat
<lea> Light DOM is easier to implement because that's the current behavior of ::slotted(), but does anyone have any feel of the implementability of the other option?
<fantasai> castastrophe: I was just making notation, but I think the nested shadow DOM is getting the slot element, not the slotted content
<fantasai> castastrophe: it renders it correctly, but when using CSS selectors
<fantasai> castastrophe: you don't have access to the slotted content until further down
<fantasai> castastrophe: So I think it's just the tag <slot> in nested shadows
<fantasai> castastrophe: so I think we need to open a new issue for how we build styles for named combinators
<fantasai> castastrophe: need to identify the named slot and the combinator in the same query
<emilio> I need to check what this `while` loop is doing then: https://searchfox.org/mozilla-central/rev/af78418c4b5f2c8721d1a06486cf4cf0b33e1e8d/servo/components/selectors/matching.rs#464-468
<Rossen_> ack TabAtkins
<fantasai> TabAtkins: So Cas, if you've been seeing the slot element matched by ::slotted in deeply nested situations, that is spec violation
<fantasai> TabAtkins: because should flatten and get the real elements
<fantasai> TabAtkins: so the slotted inside the nested part should see the original light dom elements, not the slot
<fantasai> Rossen_: It sounds like we'll end issue without a concrete resolution
<fantasai> Rossen_: let's pick it up again on Wednesday
andruud commented 1 year ago

@LeaVerou Won't this allow .a /slotted/ .b + .c to select elements outside the slotted subtree from within the shadow?

bramus commented 1 year ago

I can’t help but find /slotted/ as the proposed combinator to be “not ideal”. It’s quite long and deviates from what other combinators look like – it feels like “the odd one out” in the list of other combinators. Maybe we can find some relevant ASCII symbol to represent it? Earlier in the thread % was already proposed, but personally I was thinking of using $ for it.

tabatkins commented 1 year ago

/foo/ is the syntax we decided on some time ago for future combinators; we're basically out of good ASCII to use. Yes, it's weird because it's the first, but that'll be the case for any of them.

(The remaining ASCII glyphs are almost certainly more valuable to save for new simple selector prefixes rather than combinators.)

e111077 commented 1 year ago

It’s quite long and deviates from what other combinators look like

Well once upon a time there was /deep/ in Web Components V0 in Chrome ¯\_(ツ)_/¯

css-meeting-bot commented 11 months ago

The CSS Working Group just discussed [css-shadow-parts] Make `::slotted()` a combinator.

The full IRC log of that discussion <ntim> leaverou: making ::slotted a pseudo created a couple of issues
<ntim> leaverou: I linked to a couple of them
<astearns> [lists issues]
<ntim> leaverou: we already discussed of making it a pseudo class
<ntim> leaverou: it seems possible to make it a combinator
<fantasai> scribenick: fantasai
<fantasai> lea: which would address all of these issues
<fantasai> lea: proposed syntax for a named combinator /slotted/
<fantasai> lea: which follows a plan we had for named combinators so they're not all weird ascii
<emilio> q?
<astearns> ack emilio
<Zakim> emilio, you wanted to say that reduced-motion has various side effects in Firefox
<fantasai> lea: ::part() has similar issues, but more complicated, so figure out later
<fantasai> lea: question is can we spec this combinator without giving too much access, without leaking info about shadows to the outside
<fantasai> lea: There are lots of reactions in the issue (31 thumbs up 17 hearts) so at least positive reaction from some people
<emilio> q+
<futhark> q+
<astearns> zakim, open queue
<Zakim> ok, astearns, the speaker queue is open
<fantasai> [note for the audience, very few CSSWG issue have that many reactions]
<astearns> q+ futhark
<fantasai> emilio: This potentially makes it possible to style things that are not slotted from inside the shadow tree, right?
<fantasai> emilio: if you have /slotted/ + something else
<fantasai> lea: we'll need to provide some kind of limitation
<westbrook> q+
<fantasai> emilio: how would that work?
<fantasai> lea: that's the question
<bkardell_> q+
<fantasai> lea: we need limitation on grammar of what comes after slotted combinator
<fantasai> emilio: seems hard to do
<fantasai> lea: we could start by allowing the same grammar as argument of ::slotted() and expand from there
<fantasai> lea: I disagree with "small benefit", it does seem to solve a buch of issues
<fantasai> emilio: wrt targetting siblings
<fantasai> emilio: [didn't hear]
<fantasai> lea: right
<astearns> s/[didn’t hear]/is the thing we cannot do
<fantasai> lea: the issue is asking to style siblings that are also slotted
<fantasai> emilio: we don't have a great way of distinguishing slotted vs unslotted
<fantasai> westbrook: if a sibling isn't slotted, it isn't visible.
<fantasai> emilio: could be in a different slot
<fantasai> westbrook: if these are slotted, incredibly low specificity
<lea> q?
<lea> q+
<fantasai> westbrook: majority of custom element devs would expect, to have control over that at low specificity
<fantasai> westbrook: customer could override using standard mechanics of light DOM
<fantasai> emilio: would that change nature of sibling combinator after slotted combinator in a way that ignores slotted things? Otherwie no way to make it work
<fantasai> emilio: not in the DOM tree, not slotted
<fantasai> lea: a few questions here
<fantasai> lea: 1. can we define something like this, is it implementable
<fantasai> lea: 2. what kind of restrictions do we need to prevent breaking encapsulation
<fantasai> lea: I think we should resolve on these separately
<fantasai> emilio: if you have combinator that does same thing as pseudo, then why not not have a combinator?
<bkardell_> s/ do we need to prevent breaking encapsulation/ do we need to prevent breaking encapsulation (that's a whole other set of discussions)
<fantasai> emilio: seems hard to imagine non-problematic expansions of pseudo
<fantasai> lea: some obvious expansions that clearly don't break
<fantasai> lea: e.g. pseudo-classes or pseudo-elements that depend on that selector
<fantasai> lea: and room for expansions not posisble inside pseudo-element
<fantasai> emilio: expansions are allowed, see ::part()
<fantasai> lea: it would solve query selector out of the box
<fantasai> lea: also targetting descendants and pseudo-elements
<fantasai> lea: only question is targetting siblings
<astearns> ack fantasai
<Zakim> fantasai, you wanted to ask about order of slotting or order in light dom
<astearns> ack futhark
<lea> q-
<fantasai> futhark: This is same as content shadow DOM [missed]
<emilio> q+
<fantasai> futhark: thinking about limitations, if you add :is() and :where(), you can walk up ancestry of slotted element which is almost like :host-context() which has been turned down before
<fantasai> futhark: I would also like to point out that we support pseudo-elements on ::slotted() today, can expand the list
<astearns> ack westbrook
<emilio> q-
<fantasai> westbrook: Westbrook, Adobe, Web Components CG
<futhark> It's probably implementable in Blink since this is basically the same as ::content from Shadow DOM v0
<fantasai> westbrook: It's likely that many of us in the CG that were enthusastic in the issue, because as Web Component devs we're very excited about the possibilities of /slotted/ combinator
<fantasai> westbrook: both for reasons listed in the issue, but also for being able to leverage :has() to style based on what has been slotted
<fantasai> westbrook: this currently requires JS, and JS has to be context-dependent
<fantasai> westbrook: so this would simplify JS and open up capabilities we've not had in the past
<astearns> ack bkardell_
<fantasai> bkardell_: Complicated feelings about this proposal
<fantasai> bkardell_: on the one hand, find both ::slotted() to be awkward
<fantasai> bkardell_: and not powerful enough
<fantasai> bkardell_: but also know why we have them, and what emilio says probably true
<westbrook> Examples of JS for Slot Content Detection: https://github.com/w3c/webcomponents-cg/discussions/72
<fantasai> bkardell_: around 2012-ish, we had 2 combinators proposed to cross the shadow boundaries
<fantasai> bkardell_: and we did that, and it was implemented in Chrome, and then it was removed
<astearns> (like /deep/)
<fantasai> bkardell_: I thought that was a hasty decision, and I really do support revisiting combinators that help wiht these cases
<fantasai> bkardell_: people are building in userspace querySelector that can cross shadow boundaries, because sometimes you want that
<fantasai> bkardell_: so totally support revisiting with good use cases
<fantasai> bkardell_: also make sure we don't create something bad
<rniwa> q+
<fantasai> bkardell_: but currently I don't think it's bad, do more in this proposal to get there
<astearns> ack fantasai
<TabAtkins> Scribe+
<TabAtkins> fantasai: first question, when you say you want to target siblings, do you mean in the slotted subtree?
<lea> q?
<TabAtkins> fantasai: If so, sibling relationships as slotted, or as originally in the light dom?
<TabAtkins> (I think the answer is as slotted)
<fantasai> lea: Definitely siblings within the subtree of slotted should be accessible, one of the main use cases
<TabAtkins> fantasai: I mean like if you slot all the even children, are you seeing siblings as they're slotted, or as they were in the original light dom
<fantasai> lea: Only looking at slotted elements would break encapsulation less, but might be harder to implement
<emilio> westbrook: it seems that use case could be solved by a `slot` pseudo-class or so?
<fantasai> lea: but this is going in the weeds
<fantasai> lea: more important to see if consensus to pursue this if we can find restrictions
<astearns> q+
<TabAtkins> Basing on the slot relationships is what the use-cases want - letting you select *as if* the slotted elements were always in the position they ended up being slotted into.
<TabAtkins> fantasai: Details can matter. This suggest that you're not necessarily using a combinator, but rather looking thru a pseudo-element into a new structure.
<fantasai> lea: Not necessarily needed, not a major use case
<fantasai> lea: they need pseudo-classes, descendants, access to subtree of the slotted element itself
<fantasai> lea: but if we could restrict to access only the element itself and its subtree
<westbrook> emilio: there are definitely other paths to solving that situation, why we called it "Slot Content Detection" rather than any specific API or syntax.
<emilio> q+
<fantasai> lea: that would still solve a lot of the problems today
<fantasai> s/emilio:/emilio,/
<astearns> ack rniwa
<fantasai> rniwa: As implementer, I think short answer is no, we don't want to make this supported as a combinator
<fantasai> rniwa: because it poses significant implementation complexity
<fantasai> rniwa: I question the proposition that these are actual use cases that need solving
<fantasai> rniwa: it's often the case that when someone wants to style slotted content based on their descendants or siblings, it's often a mistake made in the DOM structure itself
<westbrook> q+
<fantasai> rniwa: at least in the cases I've seen, you need a different DOM structure, not a combinator
<lea> q?
<fantasai> astearns: Challenge that assertion
<fantasai> astearns: I think there are valid use cases that are not achievable, this is why ppl are writing JS to do it
<fantasai> astearns: and it's a lot easier to restructure DOM in your component than to write JS to achieve the right effect
<fantasai> astearns: but we are talking about a combinator that's different from other combinators, and will require safety restrictions
<fantasai> astearns: we have three implementers saying "I don't want to do this"
<fantasai> astearns: Pseudo-element doesn't do everything we want it to do
<lea> q+ another use case that came up that is addressed by this is that pepple are asking for a :has-slotted pseudo-class that applies to slots — with this, this would simply become `:has(/slotted/ *)` (can't find it now but astearns has a link to it)
<fantasai> astearns: what if we make pseudo-elements do things other pseudos don't do? And enable some of these effects?
<astearns> ack astearns
<bkardell_> s/pepple/people
<lea> q?
<lea> q+
<fantasai> emilio: asking a slot whether it has content seems useful
<fantasai> emilio: but combinator...
<fantasai> emilio: pseudo-class of a slot that says whether it's got content is a lot more straightforward
<fantasai> emilio: and that's trivially implementable
<fantasai> emilio: some use cases, like querySelector, that's a no-go to me
<astearns> ack emilio
<fantasai> emilio: same reason why ?? in shadow trees
<fantasai> emilio: the compelling use case I see here is whether or not slot has content, and that's much simpler to fix
<astearns> ack westbrook
<fantasai> westbrook: To respond to two things
<emilio> s/??/document.querySelector doesn't return stuff stuff inside shadow trees
<fantasai> westbrook: rniwa says maybe DOM structure should change
<fantasai> westbrook: but HTML parser requires certain DOM structures
<fantasai> westbrook: if user wants UL in its content, and want to manage the LIs, we can't just change the DOM structure
<fantasai> westbrook: the second use case Lea mentioned is most important
<rniwa> q+
<fantasai> westbrook: whether UL, or TABLE, or any places where DOM structures can't be change
<fantasai> westbrook: these are where we most need ability to reach into the child of the element
<fantasai> westbrook: wrt emilio's point about knowing slotted content or not is easier, yes of course
<keithamus> q?
<fantasai> westbrook: that's a second situation, not the same as the issues listed here (just you can use what lea proposes to do the same thing)
<fantasai> westbrook: but we don't think that's the center of what she's proposing
<fantasai> lea: I agree, we could add combinators and pseudo-classes and allow to be querySelector and maybe add some ad-hoc specificity rules to fix that problem
<fantasai> lea: but as we start to add these, we're just re-inventing combinators
<fantasai> lea: might be useful if implementors could elaborate what makes the combinator hard to implement
<emilio> q+
<fantasai> lea: so we can understand what we need to restrict
<astearns> ack lea
<fantasai> emilio: scenario where WebKit and Gecko differe from Blink
<fantasai> emilio: we store our data for shadow trees in the shadow root
<fantasai> emilio: there's a perf benefit to that
<fantasai> emilio: you don't add rules from inside the shadow tree
<bkardell_> to emilio's point about queryselector not crossing shadow root: by default, yes, that's the whole idea of it. That doesn't mean there shouldn't be an explcit way to do it that isn't "re-write queryselector in user space, but to cross boundaries". Any way.
<fantasai> emilio: wrt implementation complexity, things outside the shadow tree that are super far related from tehs shadow tree cannot affect styles inside it
<keithamus> q+
<fantasai> emilio: and can't match rules inside it
<fantasai> emilio: let's say you have combinator and descendant selector
<fantasai> emilio: inside shadow root you have stylsheet with .myslot /slotted/ div, can reach deep inside the slotted subtree
<fantasai> emilio: now any time anything changes in that element, we need to go find the shadow tree where that may have been
<fantasai> emilio: in order to invalidate style
<bramus> q+
<fantasai> lea: doesn't this already apply to ::slotted()?
<fantasai> emilio: yes, but only askes about the slotted element itself
<fantasai> emilio: wherease if you do it on any random element, then ...
<fantasai> emilio: And now I'm sad, you need to do a whole tree-walk to find where the rule came from
<fantasai> lea: would restricting the combinators allowed after /slotted/ help?
<fantasai> emilio: but then you can select slotted content
<fantasai> lea: basically same as nesting, first we resolve on limited syntax
<fantasai> lea: and then eventually opened up to syntax we actually wanted
<fantasai> lea: so even if almost as restricted as pseudo-element, can expand
<fantasai> lea: if we add that, then we have a hybrid between pseudo-elements and combinators
<fantasai> lea: with weird syntax
<fantasai> lea: as we figure out how to implement things, we relax restrictions
<fantasai> astearns: sounds like you would be OK with /slotted/ combinator with massive restrictions to begin with
<fantasai> astearns: you would hope that they can be lifted, but OK if not possible
<fantasai> emilio: lifting those restrictions is similar to allowign slotted pseudo
<fantasai> emilio: you may end up poking outside the shadow tree
<fantasai> emilio: not super positive that we would be able to address perf
<astearns> q?
<fantasai> emilio: and has a lot of other questions, like DOM APIs
<fantasai> emilio: what does .matches() do?
<fantasai> emilio: whether slotted matches depends on where the rule comes from
<fantasai> emilio: ? would allow you to poke inside the shadow tree structure
<fantasai> emilio: which is not anything we want
<fantasai> emilio: we don't want to expose shadow tree structure to the outside
<lea> q?
<astearns> ack rniwa
<fantasai> rniwa: agree with emilio
<emilio> s/?/.matches("foo /slotted/ div")
<lea> emilio: how did /deep/ work around these issues? I seem to remember that implementability was not the reason for removing it (but I could be wrong)
<fantasai> rniwa: challenge as implmenter is, the use cases you want are exactly the ones with perf problems
<fantasai> rniwa: consider :has(), devs really want it
<fantasai> rniwa: eventually we added it, but at great cost
<lea> q?
<lea> q+
<fantasai> rniwa: cost of adding something like this is extremely high, and benefit extremely questionable
<emilio> ack emilio
<fantasai> astearns: I'm not confident all the examples have perf problems
<astearns> ack keithamus
<fantasai> keithamus: seems use cases around built-ins, and those like UL styling LI, is this the wrong forum and do we need to think about some way to customize built-ins
<fantasai> keithamus: or do we need something other than combinator that can represent similar trees
<fantasai> keithamus: e.g. styling LI inside UL inside custom element
<fantasai> keithamus: want to slot the LIs
<fantasai> keithamus: [missed]
<astearns> ack bramus
<fantasai> bramus: we seem to be stuck on this pseudo-element selector scope it somehow, which comes to @scope
<fantasai> bramus: we have other way sto contain where your selectors reach into
<fantasai> bramus: if you could @scope onto slotted UL, your selection is contained
<rniwa> q+
<fantasai> bramus: I don't think @scope pseudo-elements are allowed, but maybe pseudo-state idk
<astearns> ack lea
<fantasai> lea: I wanted to challenge what rniwa said, if you look at the actual proposal
<fantasai> lea: he said :has() has a high cost, but also big benefit
<fantasai> lea: if you look at actual issue, lots of excitement from web components community about it
<fantasai> lea: I understand that there are challenges, but not like benefit is marginal either
<fantasai> rniwa: we can continue to disagree on this point
<astearns> ack rniwa
<westbrook> q+
<fantasai> rniwa: another point, some of the use cases could b resolved by having [missed] child descendant to a slot
<fantasai> rniwa: we've been saying we should be able to slot a non-direct descendant
<bkardell_> q+
<fantasai> rniwa: havne't pursued because of priorities, but we would support doing this
<astearns> ack westbrook
<fantasai> westbrook: if there's a link to that conversation, woudl be really awesome to see
<ntim> s/[missed]/non-direct child ?
<fantasai> westbrook: wanted to take a pass on bramus's posit, what if we could create an @scope on a slot element that talked to the light DOM tree of that slotted element represnts?
<fantasai> westbrook: seems like some of the conflict here is about capabilities of a combinator
<bkardell_> https://github.com/whatwg/html/issues/3534#issuecomment-371716571 I believe is the issue (or one of) that rniwa was referring to westbrook
<fantasai> westbrook: but if scope is on track to land, but if could say scope of a slot is the light DOM, and could control all the children of that, could we leverage those capabilities
<fantasai> westbrook: to style things
<fantasai> westbrook: and leverage growing syntax of CSS to this issue
<astearns> ack fantasai
<Zakim> fantasai, you wanted to respond to that
<rniwa> q+
<emilio> fantasai: I don't think @scope is what you want if you want to just "trap" it inside
<ntim> more precisely: https://github.com/WICG/webcomponents/issues/574
<emilio> ... has some interesting cascade implications
<fantasai> .shadowroot::slotted slotted-descendant
<emilio> ... we want a pseudo with tree structure inside
<fantasai> .shadowroot::slotted(slotted-child) > more stuff
<emilio> ... or if we want to restrict the jumping we could do ^
<astearns> ack rniwa
<fantasai> s/we want/better to use/
<fantasai> rniwa: @scope doens't solve the perf issue, because even then you have problem that some shadow root could affect your style
<fantasai> rniwa: that's the challenge that we don't want to take on without extremely high benefit
<astearns> ack bkardell_
<lea> q?
<fantasai> emilio: you need to go from a random DOM element, to the shadow tree where the style may have come from
<fantasai> emilio: if you want random elements to be styled by random slots, to find the right shadow tree you have to do arbitrary shadow tree walks
<lea> q+
<fantasai> <fantasai> OK, thanks, that helps
<fantasai> astearns: [missed]
<fantasai> emilio: pseudo-element is easier
<bkardell_> q+
<fantasai> emilio: [missed this]
<astearns> s/[missed]/is that the case for all solutions (combinator, pseudo, @scope)/
<fantasai> bkardell_: some of the things, I had similar questions about what exactly those combinators were doing exactly
<astearns> ack bkardell_
<fantasai> bkardell_: I'm not sure that I agree when you said that a non-direct chid would solve that
<fantasai> bkardell_: but I aso wanted to just publicly cheer that use case
<lea> +1 slotting non direct children wouldn't solve anyt of my use cases
<fantasai> bkardell_: because I think that use case is way beyond this one
<astearns> s/[missed this]/is that the case for all solutions (combinator, pseudo, @scope)/
<fantasai> bkardell_: would love to see it solved
<astearns> ack lea
<fantasai> lea: we've gone back into discussing other combinators and subtrees
<fantasai> lea: I think there's value in having it be a combinator even if all it does is the same thing current syntax does
<emilio> q+
<fantasai> lea: first, niceer syntax. Don't have to manage parens
<fantasai> lea: it works with querySelectorAll
<fantasai> lea: it has specificity
<fantasai> lea: even if no other extenion, it's still useful
<fantasai> lea: if ppl agree with that, we could go from there
<fantasai> lea: it wouldn't be exactly the same as pseudo-element, just a little bit more
<fantasai> lea: even if no combinators are allowed after slotted combinator, still some value -- and easier to implement
<fantasai> emilio: that still has open question
<fantasai> astearns: open questions different from blocking concerns
<fantasai> emilio: do you want shadow root to be able to query things outside the shadow tree?
<fantasai> emilio: people want it, but not normally how it works
<rniwa> q+
<fantasai> emilio: can argue about syntax
<astearns> q+
<astearns> ack emilio
<fantasai> lea: wrt DOM APIs, you can access anything if the shadow tree is open
<fantasai> emilio: but first of all, this would give you a new capability if we make :matches (???) actually follow this combinator
<fantasai> emilio: exposes shadow tree structure
<ntim> I think he meant Element.prototype.matches()
<fantasai> emilio: we can say doesn't match if you call it from outside shadow tree where the slot lives
<fantasai> lea: I suspect we have to answer these questions in general
<fantasai> lea: can't just make everything a pseudo-element
<fantasai> emilio: yes we can
<astearns> ack rniwa
<fantasai> rniwa: once again, I agree with emilio
<emilio> s/:matches/.matches()/
<fantasai> rniwa: DOM API, entire whole point of shadow DOM is [missed]
<ntim> encapsulation?
<fantasai> rniwa: so idea of finding the element that's not in your tree from another tree fundamentally goes against the design of shadow DOM
<bkardell_> q+
<fantasai> rniwa: this is such a highly important aspect of design
<fantasai> rniwa: I don't think we should introduce any API that violates it
<fantasai> astearns: not sure that creating a combinator with such a radical deviation from other combinators is easy to explain
<fantasai> astearns: limitations in some contexts where we use selectors...
<astearns> ack astearns
<fantasai> astearns: register that concern
<fantasai> bkardell_: what should be in or out wrt shadow DOM
<rniwa> q+
<fantasai> bkardell_: but disagree that we should be against
<fantasai> bkardell_: you *can* walk across, reasonable to have convenience to select across
<fantasai> bkardell_: see it in the wild
<fantasai> bkardell_: closed vs open shadow DOM exists
<fantasai> bkardell_: I think there are use cases
<lea> q?
<lea> q+
<astearns> ack bkardell_
<astearns> ack rniwa
<fantasai> rniwa: I think we can continue to disagree with you on the point, this is not the venue
<fantasai> rniwa: as far as I'm concerned as an implementer, we wouldn't consider this as a possible path
<astearns> ack lea
<fantasai> lea: rniwa, you said against the model of shadow trees
<fantasai> lea: philosophically between elements under a slotted subtree vs light DOM anywhere
<fantasai> lea: I think slotted elements should be fair game
<fantasai> lea: we know from dev surveys that the encapsulation of shadow DOM is one of the reaosns devs don't use shadow DOM
<fantasai> lea: citing that as a reason not to give devs what they need, not a good path forward
<fantasai> lea: priority of constituencies
<fantasai> astearns: Lots of discussion, but no consensus on moving forward, so suggest we take back to the issue
<fantasai> astearns: come up with some alternate approaches, work through the pros and cons of each
<fantasai> astearns: particularly from author perspective
<fantasai> astearns: and discuss this later
<fantasai> ntim: I would add looking at which use case [no audio] so we can agree on more specific syntax that addresses the perf footguns
<fantasai> astearns: so ranking the use cases, looking at most improtant first, and maybe adding for those not in the issue?
<fantasai> s/[no audio]/are perf footguns and which are not/
<fantasai> s/agree/converge/
<fantasai> s/the perf/the non-perf/
<fantasai> astearns: thanks for the discussion, we'll talk later
trusktr commented 4 months ago

I'd like to outline a concrete use case.

For example, suppose we want to have a <foo-layout> element that can style children and nested children a certain way. Suppose we use it like this (contrived names for sake of example):

<foo-layout>
  <foo-layout-item>foo</foo-layout-item>
  <foo-layout-item>bar</foo-layout-item>
  <foo-layout-item>baz</foo-layout-item>
</foo-layout>

and somehow it lays out the children in a layout. In this example, children are slotted to the default slot.

The example above is totally doable, not problem so far.

Now suppose we also want to define a nested syntax for the layout features. For example:

<foo-layout>
  <foo-layout-item>foo</foo-layout-item>
  <foo-layout-item>
    bar
    <foo-divider></foo-divider>
    baz
    <foo-divider></foo-divider>
    lorem
  </foo-layout-item>
  <foo-layout-item>ipsum</foo-layout-item>
</foo-layout>

where in this example, the <foo-divider> can be use specifically as a child of a <foo-layout-item> that is a child of the <foo-layout> to achieve a certain effect. Also imagine that <foo-divider> can be used completely outside of <foo-layout> for other use cases and with differing effects.

In that example, we currently cannot apply a special style to the foo-divider element from the foo-layout's shadowroot styles.

The style that we would want to write, from inside of the foo-layout element's ShadowRoot styles, would look like the following with hypothetical syntax that is currently not supported:

::slotted(foo-layout-item) > foo-divider {
  /* Target any foo-divider that is direct child
  of a slotted foo-layout-item, to give it some specific
  sizing within the specific layout context, for example */
}

It would be possible to write style inside of the foo-divider's shadow dom using host-context(), but that would not be ideal. Imagine that instead of <foo-divider> we actually are using <sl-divider> from Shoelace. We do not want to arbitrarily inject host-context() styles into 3rd party elements, as that would be very hacky (if their roots were closed, that would be even hackier).

We want higher-up elements (<foo-layout> in the example) to be able to style light DOM in certain ways such that the child elements do not necessarily need to be aware of every single type of higher-up element they could be nested inside of.

In real-world practice, I have been writing new elements in my app using Shoelace, and in some of those higher-level layout elements, I want <sl-divider> certain ways (as opposed to make a new divider element).

Another way to achieve this is to make N different lower-down elements to use with N different higher-up elements. What I mean is, I could make

but it would be more practical to make a single <foo-divider> that works differently (and intuitively) in various contexts, and I'd want to be able to do this with 3rd party elements easily (I am writing new layout elements, but I don't necessarily own <foo-divider>, and I don't want to have to re-implement <foo-divider> features in whole new elements).

Westbrook commented 4 months ago

@trusktr the parallels between your use case and being able to accept native elements into slots like <details>, <dt>, <ol>, <table>, <ul> run quite high. It makes me wonder at which point we could request/expect actual numbers on the cost of this sort of selecting from Implementors and not just allegorical numbers see on the initial v0 implementations in 2012.

While the underlying technique between this and @scope are likely different, it would seem that they should embody similar issues in this area. Is that API targeted to be bound by the same restrictions? What can be learned from that which may allow us to make preciously enforceable advances in this area?

I’d go so far as to volunteer time to investigate this more deeply if I could find some one versed in showing me a baseline as to how to get started working with one of the engines and a baseline of the associated selector hashing from which to work. I’d love this to be the next container queries, some thing we could never do… until we did!