Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[selectors] Pseudo-class to indicate when a slot has content #6867

Open
plinss opened this issue Dec 8, 2021 · 51 comments
Open

[selectors] Pseudo-class to indicate when a slot has content #6867

plinss opened this issue Dec 8, 2021 · 51 comments

Comments

@plinss
Copy link
Member

plinss commented Dec 8, 2021

I'm developing several web components and I'm finding a need to tell when a slot has content or not. (e.g. to apply margins or not so empty slots don't impact layout, but layout well when they do, etc.)

The :empty pseudo-class applies when the slot doesn't have direct content, even when it has slotted content, so doesn't work. Using slot:has(::slotted(*)) wouldn't detect text-only content.

Once possibility would be to redefine :empty to detect slotted content, but it may be useful to keep the current behavior to differentiate empty slots from slots with default content (and the change may break exiting content). So a new pseudo-class that only detects slotted content may be useful.

@WickyNilliams
Copy link

I have also needed this on several occasions. At the moment, you need to (at the minimum) inspect assignedNodes/assignedElements to determine this, and then add css classes to elements to modify styling. This is far from ideal for such a common use case.

A typical use case for me is to hide (display:none) a <slot> if it is has no assigned content. The reason being that i often set a slot's display to something other than contents e.g. making a slot a flex container so that i can use gap to space slotted children.

So being able to target a slot with assigned content is the most pressing use-case. Though I think it's also worth considering whether there should be separate pseudo-class for each of these states:

  • assigned content
  • default content
  • empty (i.e. no default or assigned content)

@MaxArt2501
Copy link

I'd like to point out that a :has-assigned-content pseudo-class (or better name) would be nice to have since:

  • developers would like to avoid using JavaScript for what's basically a declarative effect;
  • working around with :host(:not(:empty)) slot (for unnamed slots) or :host(:has([slot='foo'])) slot[name='foo']:
    • is verbose;
    • has content limitations (e.g. for components with multiple slots);
    • doesn't work for manual node assignment.

I'm quite keen myself to set a display value different than contents for slots, but this would also allow to style sibling or parent nodes (via :has()).

@castastrophe
Copy link

Perhaps inline with :has, :has-assigned (content could be a misnomer because it might contain text or nodes). This would enable styles such as:

slot[name='foo']:not(:has-assigned) {
   display: none;
}

A good partner to this spec would be #6620

@castastrophe
Copy link

This seems like a related request in the WICG space: WICG/webcomponents#936

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [selectors] Pseudo class to indicated when a slot has content, and agreed to the following:

  • RESOLVED: Take this up for Selectors 5, fantasai and tabatkins will come back with proposed text
The full IRC log of that discussion <emeyer> fantasai: I wanted to know if this is something we want to pursue. plinss was asked whether or not we can check to see if something has slotted content. Is this something we want to do?
<emeyer> TabAtkins: The use case makes sense and the argument is reasonable. The fact you can’t tell if text content has been slotted in leaves a whole. I say was call it ‘:slottted’
<fantasai> s/slottted/has-slotted/
<emeyer> PaulG: Is the example intended to be a slot name?
<emeyer> TabAtkins: ‘::slotted’ will select anything slotted into that slot.
<emeyer> fantasai: Tab and I should draft a proposal.
<emeyer> Rossen: Would this be level 4?
<emeyer> fantasai: No, 5
<fantasai> ACTION: Tab + fantasai to draft into Selectors 5
<emeyer> RESOLVED: Take this up for Selectors 5, fantasai and tabatkins will come back with proposed text

@AutoSponge
Copy link

in the example the ::slotted(*), the * is intended to be a selector but it may have more benefit to developers if the slot name was also queryable in a similar manner. I'm thinking of cases where a component uses multiple sibling slots.

@tabatkins
Copy link
Member

Can you elaborate on what you're thinking of? The ::slotted() pseudo-element lives on a slot element, so you can already distinguish between multiple slots. The selector argument is matched against the elements put into the slot.


As I said in the minutes above, I think :has-slotted is a reasonably straightforward name - not too long, but clearly targeted at its use-case.

@Westbrook
Copy link

@tabatkins where are you seeing the :has-slotted selector being available? Feels like the optimal usability of it would benefit from it being available all the way up to :host:has-slotted([slot=name]).

Would be we able to do something here along the lines of .assignedElements({flatten: true}) to ensure this selector is pointing to "content" and not just "possible content" (e.g. <slot> elements)?

Is there any benefit in being forward looking to the discussion in #7922 and make it a combinator? Maybe that doesn't make sense in this case...

@castastrophe
Copy link

castastrophe commented Feb 6, 2023

An interesting edge case to consider will be slots with default content as well. Does default content count as has-slotted? i.e.,

<slot>
    <p>Lorem ipsum dolor...</p>
</slot>

@MaxArt2501
Copy link

An interesting edge case to consider will be slots with default content as well. Does default content count as has-slotted? i.e.,

<slot>
    <p>Lorem ipsum dolor...</p>
</slot>

I'd say no, because that <p> isn't technically slotted. One can combine :has-slotted with :empty, if necessary.

But perhaps there may be more use cases.

@Westbrook
Copy link

Great nuance in:

<slot>
   <p>Lorem ipsum dolor...</p>
</slot>

That p comes back in .assignedElements({flatten: true}) but not .assignedElements() and is NOT available in ::slotted(p) but is in :host p, which vaguely implies that we'd need two selectors or rather one with some settings (!? 😳, not sure if there are examples of that beyond attribute selectors) to ensure we have full control over the content we're shipping here.

I'm not quite sure how you could combine :has-slotted and :empty to get the sort of outcomes desired here, specifically in that :empty is an element state selector and :has-slotted is a child state selector, they don't apply to the same elements in my imagination. Could you share more on that? I could maybe see :not(:has-slotted(...)) getting you somewhere (assuming the p wasn't queried by this selector), but then how would you capture that the p is there, unless we can also have :host:has (point of order: not in the function reference, but directly on the element) that pointed into the shadow root...interesting.

@MaxArt2501
Copy link

Now that I think of it, it's not just :empty that could come in handy, but also :has(). If I want to hide a slot that has no slotted node and no default content, I could achieve it with

slot:not(:has(*)):not(:has-slotted(*)) {
  display: none;
}

(:empty could be useful when there's just default text as content, like <slot>Hello</slot>... alas, it's brittle enough that it'd fail with just some whitespace.) :has and :has-slotted should cover all the basic uses. For example, if we have something like this, where that content can be wither passed as an attribute (for simple text) or as slotted content (for HTML content):

<slot name="title">${this.cardTitle}</slot>

I wasn't aware that default slot content is returned by .assignedElements({flatten: true}), as considering it "assigned" to any slot seems counter-intuitive to me. All in all, I'm not sure that trying to mimic the behavior of a JS method would make sense here.

@tabatkins
Copy link
Member

where are you seeing the :has-slotted selector being available? Feels like the optimal usability of it would benefit from it being available all the way up to :host:has-slotted([slot=name]).

It's on the slot, indicating that that particular slot element has slotted content in it. If you want to style the host based on whether any of its slots have slotted content, you can use :has(slot:has-slotted) to check. ^_^

An interesting edge case to consider will be slots with default content as well. Does default content count as has-slotted?

I'd say no. It's not slotted content, and in particular, it's not useful to detect slotted content in this case if this is true. That is, if :has-slotted matches a slot with default contents, then what would one do with the selector? It becomes guaranteed to match, and at that point you can just put a class on the slot and use that instead. On the other hand, having it not match when the slot is using its default content lets you make a meaningful distinction that you can only know at runtime, which is useful.

@justinfagnani
Copy link

justinfagnani commented Jun 20, 2023

We have a number of SSR users who really need this selector.

There a few reasons this is critical in some SSR implementations:

  1. They may not actually use a DOM on the server - they're just emitting strings to the HTTP response. So there may be no way to determine if an element slots into a parent's slot.
  2. The current workaround of listening for slotchange and setting a class uses imperative DOM code that doesn't work on the server, and doesn't work on initial hydration without JS.

It seems like the idea of a new selector is fairly well received... can this idea be added to an upcoming csswg meeting?

@claviska
Copy link

In the case of Shoelace, we're using imperative DOM code to do slot detection which, as @justinfagnani mentioned, doesn't work on the server. We can probably work around some of these checks by changing structure/display so empty slots collapse, but this is very uncomfortable and won't solve all of our use cases.

As it stands, without such a selector, we can't get SSR fully functional. This has, unfortunately, been a pretty big hurdle for adopters who want to use our custom elements with metaframeworks + SSR.

@michaelwarren1106
Copy link

I'm using the same imperative code mechanism as Shoelace above to detect slots having content. A css selector would be amazing.

@justinfagnani
Copy link

@keithamus

if :has-slotted is an effective alias for :has-slotted(*)

It isn't though. @LeaVerou's point here is very important:

  • the non-functional form is not simply an alias to :has-slotted(*), as the latter does not detect text nodes.

@LeaVerou
Copy link
Member

Non-functional ::slotted would make no sense: what is it targeting? Non-functional :has-slotted not only makes sense, it has its own, distinct use cases. Moreover, both in my and @claviska’s experience building custom elements, the vast majority of use cases we've come across simply required knowing if any nodes were assigned to the slot at all, whether text nodes or anything else. These were also the use cases @plinss opened this issue for.


@claviska also reminded me of a potential footgun here: does a single whitespace text node match :has-slotted? It does become slotted, but that’s a footgun in the current design, see https://x.com/LeaVerou/status/1785904086929346957
Do we reproduce this footgun or break consistency?

@justinfagnani
Copy link

both in my and @claviska’s experience building custom elements, the vast majority of use cases we've come across simply required knowing if any nodes were assigned to the slot at all, whether text nodes or anything else.

How many of these were for named slots? You can only slot elements into named slots, so :has-slotted and :has-slotted(*) would be equivalent in those cases.

does a single whitespace text node match :has-slotted? It does become slotted, but that’s a footgun in the current design, see https://x.com/LeaVerou/status/1785904086929346957
Do we reproduce this footgun or break consistency?

I think you have to reproduce this behavior. An important aspect to consistency is fallback content. A whitespace text node will prevent fallback content from showing. I don't think we want :has-slotted to disagree with fallback.

@keithamus
Copy link
Member

keithamus commented Sep 20, 2024

@keithamus

if :has-slotted is an effective alias for :has-slotted(*)

It isn't though. @LeaVerou's point here is very important:

  • the non-functional form is not simply an alias to :has-slotted(*), as the latter does not detect text nodes.

Apologies I misread. In that case both make sense to have, but again (hubris incoming) I think they can both be specified and implemented reasonably straightfowardly in unison, it's certainly how I'd prefer to tackle it.


does a single whitespace text node match :has-slotted? It does become slotted, but that’s a footgun in the current design

I'd prefer to see :has-slotted consistent with the default slotted content, warts and all. I worry smoothing over these gotchas makes it more problematic.

@sorvell
Copy link

sorvell commented Sep 23, 2024

does a single whitespace text node match :has-slotted?

Agree that this is a nasty foot gun that needs to be addressed. It's not exclusively a CSS issue since it prevents fallback content from displaying. Ideally we'd solve the core issue and not just add a CSS bandaid, but I propose the following in the interests of making concrete progress:

  1. keep has-slotted(...): and you can combine with :not(:has-slotted(...)) to detect elements not selected
  2. add has-slotted-empty: this matches the same as :empty is specified and not as it's implemented (assuming it will be possible to actually match spec?). This means it would match if only whitespace-only nodes are assigned.

If we don't get this right, the following would show a red box and that seems, to me, a bit absurd.

<x-foo>
  <template shadowrootmode="open">
    <style>
      slot:has-slotted { 
       display: block; 
       height: 20px; 
       background: red;
      }
    </style>
    <slot></slot>
  </template>
</x-foo>

@LeaVerou
Copy link
Member

Unfortunate as it may be, I am tending to agree that consistency is more important, and we need to reproduce the JS behavior, while providing some kind of mechanism to at least make these cases detectable. 😢 What that mechanism would be needs more design discussion :has-slotted-empty reads very weird to me, and I wouldn't base things on entirely unimplemented precedent).

@keithamus looking at the PR, it seems there are many more design decisions to be made with the functional version, which would make the non-functional version at least easier to specify, if not also easier to implement. That doesn't mean we won't work on the functional version — if anything getting the non-functional version in ASAP paves the way for working on the functional version, it doesn’t compete against it.

@tabatkins
Copy link
Member

@trusktr

Will :has-slotted match when the slot forwards to another deeper slot? We will need a way to distinguish slots with finally slotted content vs slots with content that is being forwarded to a deep slot.

Could you provide an example of that? I didn't think about the distinction, so I'm not sure.


@justinfagnani

I think you have to reproduce this behavior. An important aspect to consistency is fallback content. A whitespace text node will prevent fallback content from showing. I don't think we want :has-slotted to disagree with fallback.

Agree, tho I agree with Lea that it sucks. But yeah, I believe the major use-case of :has-slotted is giving it different styling based on whether the fallback is showing or not, so it needs to be consistent. Ideally we should be able to specify that a slot needs elements (or at least significant text) so it'll be fixed in all the relevant APIs at once.

In the absence of that, I think @sorvell's idea of :has-slotted-empty has merit; it's a patch, but it's simple.


re: functional form, agreed with Lea. The resolution was for the non-functional form. We can pursue the functional form as well, but it can be done separately; CSS is fine with handling a pseudo with both forms.

@sorvell
Copy link

sorvell commented Sep 23, 2024

@LeaVerou

Unfortunate as it may be, I am tending to agree that consistency is more important, and we need to reproduce the JS behavior

Yeah, in the ideal case I completely agree, but I worry that the fact that the behavior spans CSS and JS makes our typically glacial pace more cosmological here. It also seems like we're down the path of inconsistency already with :has-slotted instead of :has(:slotted) (and this is not a reason to continue in this direction!) I'm also really unsure on that status of :empty. Matching what's spec'd there seemed appealing, but that's definitely less the case if the spec is likely to adapt to what's implemented here.

@tabatkins
Copy link
Member

It also seems like we're down the path of inconsistency already with :has-slotted instead of :has(:slotted)

That's not at all inconsistent. You can't do slot > .slotted-element to select things slotted in, so slot:has(.slotted-element) won't work either. You need a new concept to be able to match that stuff.

@LeaVerou
Copy link
Member

@sorvell That is not an inconsistency, :has(:slotted) has an existing meaning that doesn’t fit what we want to do here: it would match descendants that match a (nonexistent) :slotted pseudo-class.

I doubt :empty will ever be fixed at this point as that would have web compat implications — we'd probably need to define a new one at this point. Not sure if anyone is already working on this.

@sorvell
Copy link

sorvell commented Sep 23, 2024

That's not at all inconsistent.

Yup, my mistake. It is indeed getting hard to parse these functions =)

@LeaVerou
Copy link
Member

LeaVerou commented Sep 26, 2024

Talking to @emilio yesterday, he confirmed my suspicion that :has-slotted is indeed way easier to implement than :has-slotted() (I think the word he used is "trivial"). So I would focus on getting that specced and shipped ASAP and getting a resolution to work on :has-slotted() in a separate issue.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [selectors] Pseudo-class to indicate when a slot has content.

The full IRC log of that discussion <lea> q+
<khush> keithamus: has slotted is a pseudo class. it is the opposite of definition of default slotted
<khush> has slotted means that it has been slotted
<astearns> ack astearns
<khush> we resolved on the issue to just have the ... pseudo-class
<lea> s/... pseudo-/:has-slotted pseudo-/
<khush> a bare pseudo class. not functional one
<khush> i want a functional one which takes a compound selector
<khush> lea has raised a PR of non-functional pseudo-class
<khush> could we resolve on both
<khush> lea: there are distinct use-cases for both
<khush> the non-functional version is not an alias for :has with *
<khush> have chatted with implementors, bare version is easier
<khush> i want it to move fwd
<khush> there is confusion with has(slotted)
<dbaron> s/with */with *, because the non-functional version also matches text nodes/
<khush> we don't have slotted pseudo-class, only pseudo element
<khush> there is potential confusion in terms of authors not understanding
<khush> but functionality is clear
<khush> i'm not sure if i would be in favour of fallback based naming
<khush> since we want it for other use-cases
<emilio> q+
<khush> but just mentionning
<astearns> ack lea
<khush> emilio: the bare version is very straightforward
<dbaron> s/i'm not sure if/we could name it something related to fallback, but I'm not sure if/
<astearns> ack emilio
<khush> the functional version feels hard to implement. naming suggests we're looking at descendants. what's supported would be confusing
<lea> q+ to also mention :has-slotted variant that excludes whitespace-only slotted nodes
<TabAtkins> I don't think it would be that confusing. It would be interpreted basically identically to ::slotted()
<khush> keithamus: there is confusion around what combinators would do
<dbaron> s/for other use-cases/even if there's no fallback content/
<khush> should only match the slotted element
<TabAtkins> If ::slotted(.foo) would match an element, :has-slotted(.foo) will match
<fserb> q+
<khush> emilio: it might be worth naming it after nth-child perhaps
<khush> has it been considered?
<astearns> zakim, close queue
<Zakim> ok, astearns, the speaker queue is closed
<khush> the naming is off and confusing, otherwise it's fine
<khush> the bare version is extremely simple to implement
<khush> emilio: you mentioned you wanted it to also match fallback content.
<khush> lea: if we wanna change the naming to not confuse with has, it could be fallback-show
<khush> the matching would be the same logic
<khush> q?
<astearns> ack lea
<Zakim> lea, you wanted to also mention :has-slotted variant that excludes whitespace-only slotted nodes
<khush> lea: since this has to reflect the behaviour of existing js api
<khush> even if only slotted is blank white space node it still has to match
<khush> we want a bare slotted version which includes just whitespace
<khush> is that easy to implement>
<khush> ?
<astearns> ack fserb
<khush> fserb: maybe the name would be better as revered
<khush> has-fallback-content for example
<lea> :fallback-shown is also nicely analogous to :placeholder-shown. What happens with that pseudo-class if there is no placeholder on the input?
<khush> keithamus: i like the symmetry between the 2. renaming would make them asymetrical and cause confusion

@LeaVerou
Copy link
Member

LeaVerou commented Sep 27, 2024

Update: I submitted PR #10959 yesterday, @tabatkins reviewed it today, and :has-slotted has now landed: https://drafts.csswg.org/css-scoping/#the-has-slotted-pseudo 🎉

I’ve also had chats with @emilio and @rniwa about implementation difficulty and they both said it’s trivial to do. Not sure who the Blink implementor is (@chrishtr @mfreed7 do you know?), but I imagine the answer would be similar. So, fingers crossed this could ship in browsers relatively soon, as it’s a nice quick win 🤞🏼

@mfreed7
Copy link
Contributor

mfreed7 commented Sep 27, 2024

Update: I submitted PR #10959 yesterday, @tabatkins reviewed it today, and :has-slotted has now landed: https://drafts.csswg.org/css-scoping/#the-has-slotted-pseudo 🎉

I’ve also had chats with @emilio and @rniwa about implementation difficulty and they both said it’s trivial to do. Not sure who the Blink implementor is (@chrishtr @mfreed7 do you know?), but I imagine the answer would be similar. So, fingers crossed this could ship in browsers relatively soon, as it’s a nice quick win 🤞🏼

My team could take a look - sounds rather straightforward to implement. A few questions/requests:

  1. Are there WPTs?
  2. Would you mind filing an implementation bug for us?

@keithamus
Copy link
Member

@mfreed7 happy to implement in Chrome. We have some WPTs, I'll file a bug.

@LeaVerou
Copy link
Member

Thank you @mfreed7!

  1. Are there WPTs?

Yes, though many are mixed with :has-slotted(). Here are the ones that include a bare :has-slotted:

Ideally the functional syntax should be separated into separate tests even if we resolve to add it. They are separate features and need to be tested separately.

  1. Would you mind filing an implementation bug for us?

Looks like someone beat me to it 15 hours ago! https://issues.chromium.org/issues/369883705

@mfreed7
Copy link
Contributor

mfreed7 commented Sep 27, 2024

Thank you both! Looks like there's a lot of enthusiasm for the new pseudo class. Sounds like we're off and running.

@mfreed7 happy to implement in Chrome. We have some WPTs, I'll file a bug.

In particular, thanks for this one! I can provide reviews, if needed.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [selectors] Pseudo-class to indicate when a slot has content, and agreed to the following:

  • ACTION: keithamus to open issue about ::slotted() cascade order
The full IRC log of that discussion <emilio> Rossen9: who's best to summarize this one?
<emilio> dbaron: was discussed yesterday according to github-bot
<dbaron> https://github.com//issues/6867#issuecomment-2378207271
<emilio> keithamus: didn't resolve because I had a question about the functional pseudo-class
<emilio> ... to do the same things I was talking about
<emilio> ... wrt /slotted/
<emilio> ... I think we need to pick a direction, either we look into the slotted combinator or we look into :has-slotted()
<emilio> q+
<dandclark> emilio: Did we resolve on the nonfunctional version?
<emilio> keithamus: we resolved to that prior
<emilio> ... but test were written for the functional pseudo-class
<Rossen9> ack emilio
<emilio> ... it seems the functional pseudo is more interesting to the WC community
<emilio> ... but happy to investigate the combinator
<emilio> emilio: Given :has-slotted() is dependent on whether we do the combinator, and the combinator kinda depends on what we do about the cascade order, I'd prefer to discuss the cascade order at least first, when TabAtkins is around ideally
<dandclark> emilio: We can keep the tests as tentative or I can split it
<dandclark> emilio: I think we need an issue about the cascade issue
<dandclark> ...wasn't clear that was the main complaint about the slotted selector
<emilio> ACTION: keithamus to open issue about ::slotted() cascade order

@keithamus
Copy link
Member

I think #6466 is the related issue - it talks of specificity but it looks representative of the cascade order issue.

@sorvell
Copy link

sorvell commented Oct 5, 2024

Tested :has-slotted in Chrome Canary with the "experimental web platform features" setting on.

The behavior is good for simple cases but not useful for nested composition. Perhaps this can be solved via #10771.

Content Expected Actual :has-slotted works ::slotted(*) assignedNodes() assignedNodes({flatten: true})
<glow-stuff></glow-stuff> no glow no glow yes no match [] []
<glow-stuff><input></glow-stuff> glow glow yes match [<input>] [<input>]
<template shadowRootMode="open"><glow-stuff><slot></slot></glow-stuff></template> no glow glow no no match [<slot>] []
<template shadowRootMode="open"><glow-stuff><slot></slot></glow-stuff></template><input> glow glow by luck match [<slot>] [<input>]
<template shadowRootMode="open"><glow-stuff><slot><input></slot></glow-stuff></template> glow glow by luck no match [<slot>] [<input>]

@keithamus
Copy link
Member

I'm going to Agenda+ as I'd like to capture a resolution that the current behaviour of not matching the flat tree is okay, and perhaps to resolve to investigating a way to match the flat tree (though perhaps this is :has-slotted() (#7922) or a /slotted/ combinator (#7922).

If web developers could please provide signal that the current behaviour as prototyped in Firefox and Chrome (that :has-slotted matches the non-flattened tree) is acceptable, that would help.

@Westbrook
Copy link

I really like having anything available in this area (is there any way that I can further explain the high level of important the web component developing community places on this API?). I am super excited about the implementation currently available in Chrome/Firefox (so many things we could never do have already been opened up to us with this API). However, not being able to select against the flattened tree will be a hard blocker on this API; maybe not today, maybe not tomorrow, but it will come up (much like questions around white space being "slotted") that would be better to be answered today, if only directionally.

Personally, I can certainly be appeased with the selector matching .assignedNodes({flatten: true}) only. For my experience, the ability to build complex, deeply slotted applications is and will continue to be greatly constrained by not supporting this. This also aligns it more directly with the behavior of ::slotted(). However, ::slotted() does present a questionable result in this example:

<div><template shadowrootmode="open">
  <div>
    <template shadowrootmode="open">
      <style>
        ::slotted(input) {
          color: red;
        }
      </style>
      <slot></slot>
    </template>
    <slot><input value="not styled" /></slot>
  </div>
</template></div>

Practically, we do need to support the spread.

Questions:

Does that mean multiple selectors? :has-slotted and :has-slotted-flatten could be a path, though it's interesting to need to go between the two.

Does flatten belong in the functional version? Always? Or as an option? :has-slotted(.selector !flatten) could be a path, but how this would apply to slotted text content is unclear. Also, does this mean each selector in the method get this option? :has-slotted(.selector !flatten, .other-selector), see .other-selector not feature flatten. Or would this need to be general to a single :has-slotted() usage?

Functionally, will component authors be doomed/required to "know" the slotted content depth?

<div>
  <template shadowrootmode="open">
    <div>
      <template shadowrootmode="open">
        <div>
          <template shadowrootmode="open">
            <style>
               p {
                 color: red;
               }
               slot:has-slotted(slot:has-slotted(slot:has-slotted(p))) + p {
                 color: green;
              }
            </style>
            <slot></slot>
            <p>Styled content</p>
          </template>
          <slot></slot>
        </div>
      </template>
    </div>
  </template>
  <p>Slotted content.</p>
</div>

Does a combinator approach mean that /slotted/ and /slotted-flatten/ could be made possible together, leveraged with better x-browser support for :host:has(...), and clarify both this and other situations herein? Can the combinator select on text?

Could any of these practically address selecting when content is delivered via the hole in ::slotted() outlined above?


Many thanks to all who are investing time into making this a high-quality and powerful additions to the web platform! 🙇🏼‍♂️

@sorvell
Copy link

sorvell commented Nov 4, 2024

@keithamus

I'm going to Agenda+ as I'd like to capture a resolution that the current behaviour of not matching the flat tree is okay

Based on the 3rd row of the table here, it's my opinion that:

The bare form of :has-slotted should match against the same tree (text node inclusive) as ::slotted(...).

Note, it's slightly confusing to call this the flattened tree because assignedNodes({flatten: true}) contains fallback content but ::slotted does not match it.

The basic use case for this:

  1. I have a button that needs different styling based on the presence of an icon. I will use :has-slotted to achieve this.
  2. If I want to allow a user of my composite element to take advantage of this feature, I'd do: <my-button><slot name="icon"></slot></my-button>.
  3. We need to match the "flattened"/::slotted tree to support this.

There are 2 separate issues which I think should be considered orthogonal to :has-slotted:

  1. matching out of scope distributed fallback content: there's currently no way to do this with ::slotted either so this shouldn't be uniquely handled by has-slotted.
  2. matching against whitespace only: there's also an issue with this using :empty so similarly we shouldn't uniquely handle it here.

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

No branches or pull requests