1

In my my-dropdown component in Stencil, I have this:

<slot name="filter"  />

In my HTML, I have this:

  <my-dropdown label="dropdown" size="m" filter="true">
    <select slot="filter" class="inf__dropdown-select">
      <option>One</option>
      <option>Two</option>
      <option>Three</option>
    </select>
  </my-dropdown>

The class inf__dropdown-select is defined in the stylesheet of the component, and it used to be applied to the select element inside the component.tsx, but since I need to slot it now, I replaced the select element with a single <slot name="filter" /> slot, but now I don't know how do I apply the class? If I add the class to the slot inside the component, it's not being applied. I thought adding it to the element that you are slotting would work, but it doesn't. How do I make sure the class is applied to the slotted select element?

happy_story
  • 1
  • 1
  • 7
  • 17

2 Answers2

0

Important to note: Slotted content is reflected, NOT moved!

  • <slot> content is styled by CSS in the container
    in the SO snippet below global CSS myClass
    Below <style> can be inside the component (in lightDOM !!) or outside the component.

  • and inside the component,
    the "skin" (only the first! element select) can be styled with ::slotted

  • For really deep dive, see: ::slotted CSS selector for nested children in shadowDOM slot

customElements.define("my-dropdown",class extends HTMLElement{
  constructor(){
    super()
      .attachShadow({mode:"open"})
      .innerHTML = `
        <style>
          ::slotted(select) {
            margin:3em;
          }
          div {
            background:pink;
          }  
        </style>
        <div>
          <slot name="filter">NO slot named: filter</slot>
        </div>
      `
  }

})
<style>
    select {
      font:20px arial;
    }
</style>

<my-dropdown label="dropdown" size="m" filter="true">

  <style>
    .myClass {
      border: 5px dashed green;
    }
  </style>

  <select slot="filter" class="myClass">
    <option>One</option>
    <option>Two</option>
    <option>Three</option>
  </select>
</my-dropdown>
Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49
  • So `myClass` is defined inside the stylesheet of the component? But this is what I have now, and it's not being applied.. – happy_story Jan 16 '23 at 09:18
  • Look at the code; myClass is **not** inside the stylesheet of the component! – Danny '365CSI' Engelman Jan 16 '23 at 09:19
  • But how is that going to work for me then? In my HTML, I just build the `select` and then it's shown inside the component. It's also not working for me to have the stylesheet inside the HTML. We don't import anything other than the components after we have installed the package. I can't have the stylesheet of every component in the HTML. Is there not a way to make the class come from the stylesheet of the component? – happy_story Jan 16 '23 at 09:21
  • I made one change to make that **global CSS** ``myClass`` clearer (note its in lightDOM, not in shadowDOM). If you can create a `` – Danny '365CSI' Engelman Jan 16 '23 at 09:25
  • But again, that is not going to work for me. Each component has its own big stylesheet with lots of classes. Our Stencil components are published to npm, and then installed. The only thing users do is just import the component `` and then pass few props to it, like color, size and variant. Some components will be slotted, and for them, I need to make the classes apply to them. Do you understand what I mean? I cannot have the stylesheet be in my HTML. – happy_story Jan 16 '23 at 09:31
  • See: https://web.dev/constructable-stylesheets/ Safari has it in Technical Preview now; so almost 100% support in all 3 browsers. Otherwise just import the stylesheet like you have done with CSS for years. – Danny '365CSI' Engelman Jan 16 '23 at 09:34
  • I'm sorry but I am not sure I understand. Constructable-stylesheets is just creating a new stylesheet in the HTML? But I told you this is not a solution for me. Then you say I should just import the CSS.. import it where? To the HTML? I can't, and that's not a solution. Is there any way to make it work without importing, constructing or having a stylesheet in my HTML? Is there not a way to keep the stylesheet where it is now (inside the component), but somehow make the class apply to the slotted component that is "reflected" inside the component? – happy_story Jan 16 '23 at 09:59
  • Your Web Component is in full control of the DOM; you can create/move anything you want to anywhere you want. Basic JavaScript DOM manipulation. – Danny '365CSI' Engelman Jan 16 '23 at 15:53
0

You have implemented the inf__dropdown-select style class inside shadow DOM (the style for my-dropdown), but the class is being set on the select element which is in light DOM, so the style can never be applied to the element.

To style elements inside a slot of a web component from inside the component, you use the ::slotted() pseudo-element selector inside the component's stylesheet.

For example - the stylesheet for my-dropdown:

:host {
  ::slotted(select) {
    ...
  }
  ...
}

You can also use other selectors inside the ::slotted() function such as ::slotted(.inf__dropdown-select), ::slotted(select.inf__dropdown-select) or ::slotted([slot="filter"]) depending on how specific you need to be according to the design of your component.

G. Tranter
  • 16,766
  • 1
  • 48
  • 68