Day 45: the specificity of ::slotted() content

posted on

It’s time to get me up to speed with modern CSS. There’s so much new in CSS that I know too little about. To change that I’ve started #100DaysOfMoreOrLessModernCSS. Why more or less modern CSS? Because some topics will be about cutting-edge features, while other stuff has been around for quite a while already, but I just have little to no experience with it.


When you pass an element to a web component through a <slot>, you can select that element using the ::slotted() pseudo-element and apply additional styles.

Let's take the following component. There's a paragraph in the shadow DOM and another paragraph coming from the light DOM, passed through a <slot>, and there's a global style turning the background color of paragraphs aqua.

p {
  background-color: aqua;
}
class SlotComponent extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode: 'open'});

    const content = document.createElement('div')
    content.innerHTML = `
      <p>not slotted</p>
      <slot></slot>
    `
    this.shadowRoot.appendChild(content)
  }
}

customElements.define('slot-component', SlotComponent);
<slot-component>
  <p>slotted</p>
</slot-component>

slotted

The global styles only apply to the slotted paragraph. We've already learned why in “Day 10: global styles and web components”.

If you add styles to the shadow DOM, you can see how these styles only apply to the paragraph inside the shadow DOM, but not to the slotted paragraph.

const styles = document.createElement('style')
styles.innerHTML = `
  p {
    background-color: salmon;
  }
`
this.shadowRoot.appendChild(styles)

slotted

If you want to style slotted content from within the component, you can use the ::slotted() pseudo-element.

const styles = document.createElement('style')
styles.innerHTML = `
  p {
    background-color: salmon;
  }

  ::slotted(p) {
    background-color: red;
  }
`
this.shadowRoot.appendChild(styles)

slotted

As you can see, the background color didn't change. That's because by slotting content you're not moving it from the light DOM to the shadow DOM. The nodes physically stay where they are, they're just reflected inside the web component. This also means that global document styles still apply. By using ::slotted() we can add additional styles, but by default these styles have lower specificity than global document styles.
That changes if we add !important to the mix.

const styles = document.createElement('style')
styles.innerHTML = `
  p {
    background-color: salmon;
  }

  ::slotted(p) {
    background-color: red !important;
  }
`
this.shadowRoot.appendChild(styles)

slotted

See on CodePen

Further reading

Overview: 100 Days Of More Or Less Modern CSS