Skip to content

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

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

Open
LeaVerou opened this issue Oct 19, 2022 · 31 comments
Open

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

LeaVerou opened this issue Oct 19, 2022 · 31 comments

Comments

@LeaVerou
Copy link
Member

LeaVerou commented Oct 19, 2022

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

  1. No way to get slotted elements with querySelector() or any of the other DOM APIs that take selectors (querySelectorAll(), closest(), `matches(), etc)
  2. No ability to target descendants (see also)
  3. No ability to target siblings
  4. No ability to target pseudo-elements
  5. No way to detect slots that have content (with a combinator this is simply :has(/slotted/ *)
  6. No specificity for styling (making it a combinator won't outright solve this, but gives us a way around the web compat issues)

The intended way of going from one element to another based on their relationship in the DOM is combinators. That's why combinators exist. Even introducing a combinator that is just as restricted in syntax as the pseudo-element (minus the restrictions that are inherent to it being a pseudo-element) fixes many of these problems, and paves the way for relaxing the restrictions even further down the line.

Sure, it is a wart to have a combinator with a restricted syntax, but it's only one wart (and possibly even a temporary one) which allows us to solve numerous others.

Sure, all of these can be special-cased for ::slotted(), but that adds cognitive overhead for authors and complicates implementations in a way that is (a) perpetual (special casing needs to persist forever) and (b) unbounded — these special cases will only keep increasing.

Yes, we can't get rid of ::slotted() for the foreseeable future, even if we add this, but at least we can stop special casing stuff around it, and just direct authors to /slotted/. And who knows, maybe even down the line usage could drop below the axing threshold.

Other notes:

  • Making it a pseudo-class is not an option, as discussed in [scoping] Consider making :slotted(...) a pseudo class #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
Copy link

Westbrook commented Oct 20, 2022

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
Copy link
Member Author

LeaVerou commented Oct 20, 2022

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 😊

@w3c w3c deleted a comment from Westbrook Oct 20, 2022
@EisenbergEffect
Copy link

Yes, please. A thousands times yes.

@Westbrook
Copy link

Westbrook commented Oct 20, 2022

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
Copy link

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

@LeaVerou
Copy link
Member Author

LeaVerou commented Oct 20, 2022

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
Copy link

bennypowers commented Oct 20, 2022

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
Copy link
Member Author

LeaVerou commented Oct 20, 2022

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
Copy link

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
Copy link

heyMP commented Oct 21, 2022

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
Copy link
Member

Is there overlap with #7346 ?

@LeaVerou
Copy link
Member Author

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
Copy link

heyMP commented Nov 4, 2022

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;
  }
}

Polymer/polymer#4688

@castastrophe
Copy link

@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
Copy link

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
Copy link

castastrophe commented Nov 9, 2022

@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
Copy link

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:

  • Works on ::slotted only in Safari
  • Works in all browsers on slot (when slot is given a block display value)
  • Found no examples of leaking the keyframe out of the shadow DOM (tested calling the animation from an external stylesheet.

Keyframe defined in a style tag assigned to the slot:

  • Works on ::slotted in Firefox & Chrome
  • Does not work on 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
Copy link
Member Author

LeaVerou commented Nov 9, 2022

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
Copy link
Member

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

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

@andruud
Copy link
Member

andruud commented Apr 26, 2023

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

@bramus
Copy link
Contributor

bramus commented Aug 9, 2023

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
Copy link
Member

/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
Copy link

e111077 commented Aug 16, 2023

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
Copy link
Member

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

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

@astearns astearns removed the Agenda+ label Sep 15, 2023
@trusktr
Copy link

trusktr commented Apr 9, 2024

I'd like to outline a concrete use case.

For example, suppose we want to have a 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>foofoo-layout-item>
  <foo-layout-item>barfoo-layout-item>
  <foo-layout-item>bazfoo-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>foofoo-layout-item>
  <foo-layout-item>
    bar
    <foo-divider>foo-divider>
    baz
    <foo-divider>foo-divider>
    lorem
  foo-layout-item>
  <foo-layout-item>ipsumfoo-layout-item>
foo-layout>

where in this example, the can be use specifically as a child of a that is a child of the to achieve a certain effect. Also imagine that can be used completely outside of 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 we actually are using 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 ( 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 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

  • along with to work specifically together,
  • along with to work specifically together,
  • etc

but it would be more practical to make a single 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 , and I don't want to have to re-implement features in whole new elements).

@Westbrook
Copy link

@trusktr the parallels between your use case and being able to accept native elements into slots like

,
,
    , ,
      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!

    @LeaVerou
    Copy link
    Member Author

    LeaVerou commented Sep 27, 2024

    Talking about this with @rniwa today, he said the main implementation issue is targeting other elements (descendants, children, siblings, etc.). If we syntactically scope this combinator to only target the slotted element itself by disallowing other combinators after it (and certain pseudo-classes), that is perfectly implementable.

    I think this is still worth doing:

    • ✅ It fixes the pain point around specificity, because you are now targeting the slotted element itself, not a pseudo-element representing it
    • ✅ It fixes the issue with querySelector()
    • ✅ It gives us :has-slotted() with no additional syntax (:has(/slotted/ *))
    • ✅ It gives us pseudo-elements and (most) pseudo-classes without any new syntax (closes [css-shadow-parts][css-scoping] Allow ::part after ::slotted #3896)
    • ✅ It opens up an upgrade path, so that if an implementor finds some clever way to implement the full syntax in the future, we have the syntax ready for it.

    While we could fix all these in ::slotted() theoretically, by piling more and more special syntax on top of it, this is a much more natural solution that involves way less weirdness and far better ergonomics.

    Agenda+ so we can resolve on that.

    @e111077
    Copy link

    e111077 commented Sep 27, 2024

    Great additional context @LeaVerou! If these would be the limitations, I’d be heavily in favor of a “different” selector like /slotted/ simply because the behavior is different from the current set of combinators – disallowing subsequent combinators.

    Though I’d wonder if /combinator/ is the future of new combinators, it may muddle behavior affordances for future /combinators/? Counterpoint, there has to be a first /combinator/

    @Westbrook
    Copy link

    This is super interesting, and if it actually opens a fast lane for this, count me in!!! 🚀

    Did Ryosuke give any thoughts on how to restrict the compound-selector in :has()? Compound selectors, like:has() inherently and /slotted/ * for having the don't seem to have limitations anywhere else and have caused some implementors to question the use of :has() in other shadow DOM-relative selectors. I will note that, intentional or not, Ryosuke's team at WebKit has been the most lenient in this area with their spectacular support for all manner of :host(:has(...)) selectors: https://wpt.fyi/results/css/css-scoping?label=master&label=experimental&aligned&q=host-has-0.

    @css-meeting-bot
    Copy link
    Member

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

    The full IRC log of that discussion lea: Right now, the syntax aorund shadow dom includes a lot of pseudos - ::part(), ::slotted()
    lea: ::slotted() is probably the easiest to "fix". Bunch of problems with it - can't use querySelector(), can't target descendants
    lea: I udnerstand this seems desirable to avoid breaking encapsulation.
    lea: But so many use-cases - wrappers, etc - that need the ability to select past.
    lea: No way to target siblings of slotted elmeents, their pseudo-elements, etc
    q+
    lea: I had thought this was why their styling had low specificity, but that's actually a different issue
    lea: There's no way to detect slots that have content....
    q+
    lea: if "slotted" was a combinator you could use :has()
    lea: In general, ::slotted() seems like a wart we introduced to get things done quickly, but it causes a lot of problems
    lea: Ultimately, the relationships between elements is what combinators are for
    lea: Using pseudo-elements introduces a lot of problems
    lea: It seems like turning the syntax into a combiantor would solve a lot of problems, even if we have to apply restrictions for now
    lea: Seems better than piling on more syntax over what pseudo-elements are.
    lea: We can add more syntax, extend definitions, etc, but ultimately what people need is a combinator.
    TabAtkins: I think `::part()` has still good justification for its existance
    ... slotted was indeed kind of a workaround
    ... I agree if we could do this it'd be good
    emilio: I think the main restriction that ::slotted() being a pseudo gets you is that arbitrary elements in the light dom can't be targeted by shadow roots
    emilio: that's a perf consideration
    emilio: don't wnat to have to look thru arbitrary ancestor chains to know what nodes you need to pull styles from
    emilio: ::slotted() being a pseudo-element guarantees you that you only need to look at the shadows you're slotted to
    q?
    q+
    ack TabAtkins
    emilio: So I think that's a strong justification. I understand the limitations tho.
    ack emilio
    ack emilio
    ack lea
    lea: I understand it might be tough to do impl-wise due to assumptions made so far.
    lea: a path forward might be to make it a very restricted combinator at first, syntactigc sugar over the pseudo-element
    lea: whatever argument you can put inside of ::slotted(), that's all that's allowed after it
    q+
    lea: authors then could migrate over to using the new syntax. the longer we wait the more existing CSS we'll have to deal with.
    lea: so paving the way with a restricted combinator is a way forward
    ack emilio
    emilio: I'd lvoe to hear other impl takes
    q+
    emilio: this doesn't look like the kind of restrictions you can lift
    emilio: to figure out how to style a node that's a descendant or sibling of something slotted, you'd need to start arbitrarily walking the ancestor chain to find your closest ancestor that might be slotted
    emilio: not even clsoest, all of them
    emilio: I don't think this is the kidn of restriction we can really depend on
    q-
    https://github.com//issues/7922#issuecomment-2380098440
    TabAtkins: ryosuke had some comments
    scribe+
    TabAtkins: the usefulness of it as lea listed is taht some of other syntactic parts of css work automatically
    … all the pseudoe classes and elements of a slotted one would be available
    emilio: but those already work>
    lea: queryselector will
    s/ lea: queryselector will/ lea: queryselector too/
    tabAtkins: i dont think you can do ::slotted::before
    emilio: 99% sure
    iank_: (missed)
    TabAtkins: the qs() issue is rather frustrating too
    s/(missed)/I don’t think our style experts have seen this, but I can ping them/
    emilio: we would never be able to return textnodes
    TabAtkins: we dont
    emilio: (missed)
    q?
    lea: i have been in csswg meetings where certain things that shipped today that the consensus was that it would never fly
    … there was a time where :has() was never possible
    … restrictions on what is impl. change over time
    … we should not design syntax around current limitations
    … we can expand it later
    … we did with nesting
    … at the time, nesting was designed without lookahead
    q+
    … and we designed a syntax that was compatible with unbounded lookahead
    … and eventually andruud and others had a good idea and it became possible
    … thats why i think to move to a syntax that would allow the expansions
    ::slotted() pseudos work afaict: https://www.software.hixie.ch/utilities/js/live-dom-viewer/saved/13451
    … (missed)
    s/(missed)/adding more warts of what pseudo elements were designed to do
    … we are using them more and more to represent elements in the DOM
    … its adding difficulty to learning CSS
    q+
    astearns: would push back on that. its not that we cant expand the pseudo syntax but that it would be likelye a one-off thing
    lea: thats what I am saying
    ack astearns
    … ideally its better solved in a way with existing things
    astearns: it would be still a little werid to have this combinator wher eyou might expect to be able to use it but then learn that there are limitations
    lea: its a tradeoff
    emilio: can i push back?
    ack emilio
    emilio: push back on some of the things
    emilio: it feels that turning it into a combiantor without a concrete plan to change the restrictions - it's not making CSS easier
    emilio: If you have combiantor you can only use at very specific positions - you can't use Nesting, :has(), etc
    emilio: so I dunno, I just don't feel like this is a meaningful improvement over the status quo
    emilio: I get the querySelector() point. you'd need to special-case ::slotted() to return elements from another tree, currently qS() never does. But you could make it work.
    q+
    emilio: So I don't see this as great answer unless there's a concrete path
    lea: We didn't have a concrete path for nesting lookahead...
    emilio: when we ahve a plan, we can go ahead. just don't see it useful until then.
    TabAtkins: looking over the list of things again, th ecurrent restrctions
    … some of them have been lfited, not what we can put pseudo after ::slotted is nice
    s/We didn't have a concrete path for nesting lookahead.../We didn't have a concrete path for the north start nesting syntax either when we resolved to get partway there/
    … if we can fix qs to refer to slotted tha twould fix that part
    … the specificity one is prolly not compat when we try to fix
    … we still need has-slotted that you do want to know if there are text nodes in it
    q+
    … if we just fix queryselector and think about specificity we might be sufficiently fine
    ack TabAtkins
    emilio: i dont see the tradeoff to making it a combinator be particularly good
    lea: either way we go,
    … if we continue pushing on the syntax we have
    … there is going to be at least 1 word here
    … not going to be elegant
    s/word/wart/
    ; if we continue pushing on the pseudo-element route, thre is multiple types of weirdness
    … every DOM api that takes selector must also accept it
    … if authors want to provide a way for their users, they also have to special case that
    … and parse selectors on their won
    q+
    ack lea
    … yes, we can special case specificity fixing cascade order is not enough
    … and who knows what else to special case down the line?
    … adding more warts to the language
    … which we have to deal with forever
    … OTOH if we go combinator route there is only 1 word which we can get rid of later on
    … for me it seems that the combinator is in favor
    …when trying to see the big picture
    … but ymmv
    s/word/wart/
    ack fantasai
    fantasai: two comments. we shoudl keep in mind the experience of the author, so giving them a consistent syntax on a consistent model makes the platform more usable. that's worth doing if it's possible, it's not a no-op even if it seems that way from an engine perspective
    fantasai: in the distant past we discussed putting elements/combiantors *after* a pseudo-element to indicate that they're processed relative to the pseudo; kinda the pseudo-element is a combinator
    fantasai: ::region(foo) > a
    ack emilio
    q+
    emilio: I'm just not sure I agree that having a combinator ... it won't allow us to remove the pseudo, and a lot of the things you've mentioned work that way with ::slotted() already
    s/::region(foo) > a/.foo::nth-fragment(2) a/
    emilio: a bunch of special cases you need to special case *anyway* because it's a combinator and pointing to a different tree
    emilio: I'm seeing it's two warts. You have a pseudo-element, and you have ac ombinator, and they both ahve restrictions
    emilio: like `foo /slotted/ bar { div {...}}` needs to not work
    lea: you need to invalidate that not at the syntax level, but at selector resolution
    emilio: right, that's what pseudo-elements do today, it's the same restrictions
    :host: /slotted/ .foo { &.bar { } }
    lea: that's not possible with pseudo-elements
    emilio: that's a third new wrinkle
    lea: not a wrinkle, it's the same as what selectors do today
    astearns: I dont' think people will be convinced today. Take it back to issue, emilio outline problems with combinator restrictions. We can see how extending querySelector() to allow ::slotted() might look so we can balance things against the proposal.
    emilio: lea, you asked about pseudo-elements somehow forcing people to parse selectors in content
    lea: say you have a web component that references other elements. a tooltip for something.
    lea: either you do IDREF like HTML, or take a selector in an attribute
    lea: Very useful for libraries to take selectors. There's a mutationObserver wrapper that uses selectors, for instance
    lea: If we have to special-case certain pseudo-elements, authors also have to do so
    lea: So I think it's better to have combinators return elements
    lea: i think it's a good design principle to strive towards
    emilio: I can buy that. But the pseudo-element isn't going to go away, so authors will need to special-case it anyway.
    emilio: and they'll need to special-case the /slotted/ as well due to the restrictions
    astearns: another thing we could ahv eint he issue discussion is whether there is a feasible path to removing the restrictions
    emilio: yeah, i'm fine *if* we ahve a path to improve this so /slotted/ is a regular combinator, I just dont' see it
    lea: we don't have to remove all, would probably be easier to let it look into descendants than looking everywhere
    emilio: not really, sibling would be easier i think
    emilio: what the browser needs is, giving node X, find all the places rules styling X will come from
    q-
    emilio: So alan's plan seems reasonable. Figure out what restrictions could be lifted, evaluate from there.
    astearns: If we do the combinator we dont' have to lard up the pseudo-element
    astearns: closing this for now, discuss more in issue
    lea: we also need to listen to the author community, this is something the WC community has been very excited about
    emilio: I think that's mostly a misunderstanding about the combinator restrictions fixing some things
    emilio: I think a lot of the demand is based on the combiantor allowing this to work how they want, like it's interleaved with the outer document rules. but that's not how it's going to work.
    emilio: rules from different trees just don't interleave, they're either fully before or after
    emilio: I do think that's osmething we want to fix, but that's a separate issue

    @LeaVerou
    Copy link
    Member Author

    LeaVerou commented Feb 21, 2025

    To summarize the F2F discussion, @emilio was uncomfortable with the idea of a combinator that takes a limited syntax, and wanted us to have a plan for how we may be able to relax the limitations before we resolve to do it.

    I edited the OP to add my counterarguments:

    The intended way of going from one element to another based on their relationship in the DOM is combinators. That's why combinators exist. Even introducing a combinator that is just as restricted in syntax as the pseudo-element (minus the restrictions that are inherent to it being a pseudo-element) fixes many of these problems, and paves the way for relaxing the restrictions even further down the line.

    Sure, it is a wart to have a combinator with a restricted syntax, but it's only one wart (and possibly even a temporary one) which allows us to solve numerous others.

    Sure, all of these can be special-cased for ::slotted(), but that adds cognitive overhead for authors and complicates implementations in a way that is (a) perpetual (special casing needs to persist forever) and (b) unbounded — these special cases will only keep increasing.

    Yes, we can't get rid of ::slotted() for the foreseeable future, even if we add this, but at least we can stop special casing stuff around it, and just direct authors to /slotted/. And who knows, maybe even down the line usage could drop below the axing threshold.

    I also updated the OP to:

    1. List other DOM APIs that take selectors (querySelector(), querySelectorAll(), closest(), matches(), etc) — it's not just qSA, all of these would need to be special cased.
    2. Correct the statement about specificity (I have since learned it is unrelated and has to do with how shadow CSS is treated as a separate, lower priority origin.

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Projects
    Status: Wednesday afternoon
    Status: Friday Afternoon
    Development

    No branches or pull requests