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

Complex selectors in ::slotted() and :host()? #5

Open
Westbrook opened this issue Jan 27, 2021 · 20 comments
Open

Complex selectors in ::slotted() and :host()? #5

Westbrook opened this issue Jan 27, 2021 · 20 comments

Comments

@Westbrook
Copy link
Collaborator

Now that all browsers support complex CSS selectors inside of :not(), what prevents the same within ::slotted() or :host()?

Browser announcements for reference:

Naively, this seems like the same internals would go into both resolutions, so much so that the idea of not just getting deep children, but resolving cascades up to next shadow root seem like they should just come for free now.

Example:

<parent-element>
    #shadow-root
        <div class="wrapper">
            <child-element>
                #shadow-root
                    <style>
                        ::host(.wrapper *) {
                            color: blue;
                        }
                        ::slotted(.wrapper *) {
                            color: red;
                        }
                        ::slotted(p a) {
                            color: green;
                        }
                    </style>
                    <p>This should be blue.</p>
                    <slot></slot>
                <p>This should be red...</p>
            </child-element>
            <child-element>
                #shadow-root
                    <style>
                        ::host(.wrapper *) {
                            color: blue;
                        }
                        ::slotted(.wrapper *) {
                            color: red;
                        }
                        ::slotted(p a) {
                            color: green;
                        }
                    </style>
                    <p>This should be blue.</p>
                    <slot></slot>
                <p>This should be red...</p>
            </child-element>
        </div>
        <child-element>
            #shadow-root
                <style>
                    ::host(.wrapper *) {
                        color: blue;
                    }
                    ::slotted(.wrapper *) {
                        color: red;
                    }
                    ::slotted(p a) {
                        color: green;
                    }
                </style>
                <p>This should NOT be blue.</p>
                <slot></slot>
            <p>This should NOT be red... but, <a href="#">This should be green</a></p>
        </child-element>
</parent-element>

Use cases:
The ability to use :host(.wrapper *) aligns with the use of complex selectors on top of the idea of self references like :host([open]) etc. while allowing for the capabilities of :host-context() that never found footing outside of Blink, without the need for a completely new selector. This sort of functionality gives a component author the ability to outline specific realities about the delivery of their component is specific context. This is similar to how the details > summary:first-of-type element gets upgraded to have toggle UI: https://codepen.io/Westbrook/pen/QWGLdxP

This sort of .parent * resolving selector is interesting, and would seem like you'd want to apply it to content in a ::slotted() selector for all the same reasons, thought I do see how having the extended capabilities in the :host() selector outlined above would allow you to make this same ::slotted(.wrapper *) selection with :host(.wrapper *), in the case that we can't have it all. Certainly the more important addition of complex selecting in ::slotted() has to do with the ability to target styles on non-direct children.

A great power feature of an element with shadow DOM is its ability to encapsulate a world of both functionality and styles. Slotted content puts a really cool inside/out, outside/in twist on that. The ability to progressively enhance content with a custom element, turning vanilla content into vibrant content currently falls a little short when the encapsulated space can't really bring descendent content into the family without addressing it with styles from JS. While the content being slotted means we do have the means to style it from the outside, the benefit of slotting it into an element with shadow DOM should be that we're not required to. Imaging the child-element in the above example is an article or aside or callout that should take on a new background color when upgraded, without the ability to control the color of the anchor tag grandchild element it's possible that the content can become unaccessible without requiring advance knowledge of custom element usage (if a custom element and the :not(:defined) selector is even available) for a consuming developer to ensure that their content will be delivered appropriately.


It's possible that this issue belongs in https://github.com/WICG/webcomponents/issues, and I'm happy to move it if so. However, I wanted to start by asking this as an opportunity to learn about the possible reasoning behind the currently available APIs and gather community interest in the capabilities it unlocks before attempting to more formally position it as a proposal for standardization.

@daKmoR
Copy link
Collaborator

daKmoR commented Feb 17, 2021

there is a lot in this issue - maybe we can split it and only focus on one part at a time? 🤔

I personally would be most interested in ::slotted(.wrapper .some .sub-selector) and if we can push something in this direction - maybe we should make it a dedicated issue?

@Westbrook
Copy link
Collaborator Author

@mfreed7 mentioned opening a bug to get the current state of possibility in Chrome. Do you have any thoughts on this, Mason? Maybe we could get that bug and this issue associated for cross-tracking purposes? OR if nothing else linked so those of us interested can star it?

@mfreed7
Copy link

mfreed7 commented Apr 15, 2021

Took me a while to find it, but I opened this Chromium bug to discuss. See this comment in particular for the likely reason complex selectors won't be supported here.

@abdonrd
Copy link

abdonrd commented Jul 21, 2021

@Westbrook thanks for the issue!

As @daKmoR, I also really interested in ::slotted(.wrapper .some .sub-selector).

Create a component library based on Web Components gets a bit tricky with a limitation like this.
We need it for "simple" examples like these:

<x-card>
  <div slot="title">The title</div>
  <p>Text one with <a href="#">link</a>!</p>
  <p>Text two!</p>
</x-card>
<x-banner>
  <div class="bx--grid"> <!-- `bx--grid` is a class to position the content at application level -->
    An announcement with a <a href="#">link</a>!
  </div>
</x-banner>

@Westbrook
Copy link
Collaborator Author

These use cases are great @abdonrd! We’re just now starting the process of developing a “Pain Points and Day Dreams” sort of report to present to the WC working group and hope you’ll support us in getting all the important issues clearly outlined to share therein.

@Tansito
Copy link

Tansito commented Jul 22, 2021

Complementing a little bit the last comment from @abdonrd and if this can be useful for you @Westbrook. Another common use case that Abdón and me faced before is the use of <span></span> to add not common symbols as it happens in a research environment for mathematics or physics (to add a more concise case).

<x-card>
  <div>
    Lorem ipsum <span>Symbol here</span> dolor sit ame
  </div>
</x-card>

And this can be extrapolable to images, for example. Or another element that needs to fit in a specific space/position inside a text.

@davatron5000
Copy link
Collaborator

Allowing complex selectors inside ::slotted() would go a long way. Any assemblies that require a combination of nested elements would benefit from this.

<fancy-table>
  <table>
    <tr>
      <td>Style me differently after fancy-table is connected</td>
    </tr>
   </table>
</fancy-table>

A more realworld example of this might be providing responsive behaviors to tables a la Filament Group's TableSaw library. http://filamentgroup.github.io/tablesaw/demo/stack.html

@frostyweather
Copy link

+1 to this. A pretty good example of this is the Duet DS table https://www.duetds.com/components/table/

@WickyNilliams
Copy link

Oh hey I built the duet table component 😄 I had to opt out of shadow dom when building that component, specifically because it is not possible to style slotted deep table elements like td etc

In short +1 to this!

@LeaVerou
Copy link
Member

LeaVerou commented Jul 9, 2022

Stumbled on another use case today:

<button-group>
	<button>a</button>
	<button>b</button>
</button-group>

<button-group vertical>
	<button>a</button>
	<button>b</button>
</button-group>

You want to style the slotted buttons differently for horizontal and vertical button groups, which doesn't seem possible without this.

@WickyNilliams
Copy link

Would

:host([vertical]) ::slotted(button)

Not work?

@LeaVerou
Copy link
Member

I expected that would work too, but nope (at least in Chrome). It appears :host and :host() cannot do descendants, and neither can ::slotted().

@Westbrook
Copy link
Collaborator Author

@LeaVerou check this out: https://codepen.io/Westbrook/pen/VwXjeoR

Is there something more complex that you're trying to do? If so, the full use case would be a great addition to this issue.

@eduardomecchia
Copy link

As @Westbrook showed in this CodePen, I can't seem to select parts after selecting the slotted last of type of a custom element.
https://codepen.io/Westbrook/pen/MWVbRax

@LeaVerou
Copy link
Member

LeaVerou commented Jul 15, 2022

@LeaVerou check this out: codepen.io/Westbrook/pen/VwXjeoR

Is there something more complex that you're trying to do? If so, the full use case would be a great addition to this issue.

Well, this is embarrassing. I was tripped up by specificity like a newb. 😳 Did not expect that ::slotted() would not contribute anything to specificity and things like ::slotted(button:not(:first-of-type)) would be overridden by author button styles (demo), so I had assumed the selectors weren't working at all (and somehow missed them being applied in the dev tools, no idea how). So… yeah, nothing to see here, move along. 😛

Edit: Though that might be a Chrome bug, since per spec:

The specificity of ::slotted() is that of a pseudo-element, plus the specificity of its argument.

@WickyNilliams
Copy link

Yeah it's a constant annoyance for me that the specificity is so low. It's the one instance where I regularly use !important to demarcate essential styles

@LeaVerou
Copy link
Member

Yeah, I also ended up using !important 😕 But check out my edit above: could it be a bug or am I misreading the spec? Though all browsers seem to agree on 0 specificity, so even if it is a bug, it's more likely that we change the spec than browser behavior at this point. 😕

@Westbrook
Copy link
Collaborator Author

Westbrook commented Aug 19, 2022

I'm quite biased, but I feel that :host(:has(...)) pointing into the light DOM (in Safari, currently, the spec hasn't fully landed in Chromium): https://codepen.io/Westbrook/pen/PoRVbQo kind of/really opens the door for these sorts of capabilities, right? 😜

@chrisdholt
Copy link

I'm quite biased, but I feel that :host(:has(...)) pointing into the light DOM (in Safari, currently, the spec hasn't fully landed in Chromium): https://codepen.io/Westbrook/pen/PoRVbQo kind of/really opens the door for these sorts of capabilities, right? 😜

It certainly does @Westbrook. I was a bit shocked to see that Safari was alone with support here. All I need to do is change the padding of the host element when an SVG is slotted in any of the slots.

<foo-button>
   <svg></svg>
</foo-button>

<foo-button>
   <svg slot="start"></svg>
</foo-button>

<foo-button>
   <svg slot="end"></svg>
</foo-button>

:has() within the :host does the trick perfectly. Back to JS it seems 😢...

:host(:has(svg)) { 
    padding: var(--different-padding);
}`

@Noleli
Copy link

Noleli commented Dec 14, 2023

I'm quite biased, but I feel that :host(:has(...)) pointing into the light DOM (in Safari, currently, the spec hasn't fully landed in Chromium): https://codepen.io/Westbrook/pen/PoRVbQo kind of/really opens the door for these sorts of capabilities, right? 😜

I was chatting with @Westbrook about this a bit this morning since I recently stumbled upon a use case: it could be used to progressively enhance form controls like radio buttons.

Blog post
Codepen

The sense I get from this discussion is that it’s already to spec, just not well supported. I’m not sure if this adds anything to the discussion, but any case, figured it couldn’t hurt to share here :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests