Skip to content

Styling Slotted Content

The ::slotted(selector) pseudo-element lets you write CSS rules inside the shadow root that target light-DOM nodes projected into a <slot>. When a consumer places a <p> or <strong> inside your custom element, those nodes remain in the light DOM but are visually rendered inside the shadow tree — ::slotted() is how you style them from within.

::slotted(p) { color: steelblue; }
::slotted(strong) { color: #e11d48; }

The rules above live inside the component’s shadow <style> block. Any <p> or <strong> element that the consumer slots into the component will receive those styles.

::slotted() can only match top-level slotted nodes — the immediate children of the host that land in the slot. It cannot reach into the subtree of a slotted element:

  • ::slotted(div span) does not work — you cannot target a <span> nested inside a slotted <div>.
  • Only compound selectors with a single slotted element are valid.
  • Specificity of ::slotted() is low — equivalent to (0,0,1,0) — so page styles on the same element can win.

If you need deeper control over slotted subtrees, restructure your HTML to slot the innermost elements directly, or pass CSS custom properties into the shadow root from the page.

OptionBenefitCost
::slotted() from the shadow rootThe component author can set default styles for slotted content, so consumers don’t have to write any CSS themselvesOnly matches top-level slotted nodes directly — can’t reach into a subtree, and low specificity means page CSS overrides it easily
Let the page style light DOM itself / pass in custom propertiesConsumers already have full control over styling their own content since it’s light DOM — no dependency on ::slotted() at allThe component author loses control over default appearance if no baseline is set — slotted content may look inconsistent with the component
  • Expecting a descendant selector inside ::slotted() to work — writing ::slotted(p) span {} expecting to style a span nested inside a slotted p. This syntax is invalid — ::slotted() only matches the top-level slotted element itself, not its descendants.
  • Using a deep compound selector like ::slotted(div span) — expecting to target a span nested inside a slotted div, but the spec only allows matching a compound selector at the same level as the slotted node.
  • Forgetting ::slotted() has low specificity — equivalent to only (0,0,1,0), so a page style directly targeting the same element can silently win, with no !important needed.

💡 Real-world examples

Shoelace — a component like <sl-card> uses ::slotted() to set default margins on content the consumer slots in, in case they didn’t set their own style, accepting the limitation that it can only adjust top-level nodes.

Adobe Spectrum (Spectrum Web Components) — chooses to pass CSS custom properties into some components instead of relying on deep ::slotted(), because the structure of slotted content is often too complex for a shallow selector to handle.

What does ::slotted(p) target?
Can ::slotted(div span) target a span nested inside a slotted div?
Where is the ::slotted() rule written?