Skip to content

::part and ::slotted

This lesson explains ::part and ::slotted with clear examples, use cases, and best practices so you can build reusable Web Components confidently.

What Are CSS Shadow Parts?

CSS Shadow Parts are styling features that help Web Components expose safe styling hooks from inside Shadow DOM. The two commonly used selectors are ::part() and ::slotted().

The ::part() selector allows outside CSS to style selected internal Shadow DOM elements. The ::slotted() selector allows Shadow DOM CSS to style elements passed into a component through slots.

Selector Description
::part() Styles exposed internal Shadow DOM elements from outside
::slotted() Styles light DOM elements passed into a slot
part Attribute used to expose an internal element for styling
<slot> Placeholder where external content is rendered
Main Benefit Controlled styling without breaking Shadow DOM encapsulation

Basic ::part() Example

class AppButton extends HTMLElement {
  constructor() {
    super();

    this.attachShadow({ mode: "open" });

    this.shadowRoot.innerHTML = `
      <button part="button">
        <slot>Button</slot>
      </button>
    `;
  }
}

customElements.define("app-button", AppButton);
app-button::part(button) {
  background: #0d6efd;
  color: white;
  padding: 0.5rem 1rem;
  border-radius: 0.5rem;
  border: none;
}

The internal button is exposed using part="button". External CSS can style it with app-button::part(button).

Basic ::slotted() Example

class InfoCard extends HTMLElement {
  constructor() {
    super();

    this.attachShadow({ mode: "open" });

    this.shadowRoot.innerHTML = `
      <style>
        ::slotted(h2) {
          color: #0d6efd;
          margin-bottom: 0.5rem;
        }

        ::slotted(p) {
          color: #495057;
          line-height: 1.6;
        }
      </style>

      <article>
        <slot></slot>
      </article>
    `;
  }
}

customElements.define("info-card", InfoCard);
<info-card>
  <h2>Card Title</h2>
  <p>This paragraph is passed into the slot.</p>
</info-card>

The ::slotted() selector styles elements provided by the consumer and rendered inside the slot.

::part() vs ::slotted()

Feature ::part() ::slotted()
Styles Internal Shadow DOM elements External light DOM content passed into slots
Used From Outside the Web Component Inside Shadow DOM CSS
Requires part attribute <slot> element
Example app-card::part(title) ::slotted(h2)
Best For Public styling API Styling projected slot content

Complete Example Using ::part() and ::slotted()

class ProfileCard extends HTMLElement {
  constructor() {
    super();

    this.attachShadow({ mode: "open" });

    this.shadowRoot.innerHTML = `
      <style>
        .card {
          border: 1px solid #dee2e6;
          border-radius: 1rem;
          padding: 1rem;
          background: #ffffff;
        }

        ::slotted(p) {
          color: #495057;
          line-height: 1.6;
        }
      </style>

      <article class="card" part="card">
        <header part="header">
          <h2 part="title">
            <slot name="title">Profile</slot>
          </h2>
        </header>

        <section part="body">
          <slot></slot>
        </section>

        <footer part="footer">
          <slot name="footer"></slot>
        </footer>
      </article>
    `;
  }
}

customElements.define("profile-card", ProfileCard);
<profile-card>
  <span slot="title">John Doe</span>
  <p>Frontend Developer with Web Components experience.</p>
  <small slot="footer">Available for projects</small>
</profile-card>
profile-card::part(card) {
  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.08);
}

profile-card::part(title) {
  color: #0d6efd;
  font-size: 1.5rem;
}

profile-card::part(footer) {
  color: #6c757d;
  font-size: 0.875rem;
}

This example uses ::part() to style exposed internal elements and ::slotted() to style content passed into the default slot.

How CSS Shadow Parts Work

Step What Happens
1. Create Shadow DOM The component keeps internal markup encapsulated
2. Add part Expose selected internal elements for external styling
3. Use ::part() Style exposed internal elements from outside CSS
4. Add <slot> Allow external content to render inside the component
5. Use ::slotted() Style slotted content from inside Shadow DOM CSS

::slotted() Limitation

<info-card>
  <div>
    <p>Nested paragraph</p>
  </div>
</info-card>
::slotted(div) {
  color: #0d6efd;
}

/* This will not style deeply nested p directly */
::slotted(div p) {
  color: red;
}

The ::slotted() selector only targets the direct element assigned to a slot. It does not deeply style nested children.

When to Use ::part() and ::slotted()

  • Use ::part() when consumers need to style internal Shadow DOM elements.
  • Use ::slotted() when the component needs to style projected slot content.
  • Use CSS Custom Properties for simple theme values like colors and spacing.
  • Use ::part() for element-level customization.
  • Use ::slotted() for headings, paragraphs, icons, or content passed into slots.

Common Use Cases

  • Expose a button, label, input, or icon using part.
  • Style card header, body, footer, and title using ::part().
  • Style user-provided headings or paragraphs using ::slotted().
  • Build design system components with public styling hooks.
  • Keep internal classes private while still allowing customization.
  • Combine CSS Custom Properties, ::part(), and ::slotted() together.

Best Practices

  • Expose only necessary internal elements with part.
  • Use meaningful part names like button, title, header, and footer.
  • Treat part names as a public API.
  • Use ::slotted() only for direct slotted elements.
  • Use CSS Custom Properties for design tokens.
  • Document all supported parts and slots.
  • Avoid exposing internal implementation details unnecessarily.

Common Mistakes

  • Trying to style Shadow DOM elements with normal external CSS selectors.
  • Forgetting to add the part attribute before using ::part().
  • Expecting ::slotted() to style deeply nested children.
  • Using unclear part names like box1 or item2.
  • Changing part names without considering backward compatibility.
  • Using ::part() when CSS Custom Properties would be simpler.
  • Not documenting public styling hooks for component consumers.

Key Takeaways

  • ::part() styles exposed internal Shadow DOM elements.
  • ::slotted() styles content passed into slots.
  • The part attribute creates public styling hooks.
  • ::slotted() only targets direct slotted elements.
  • Use CSS Shadow Parts to balance styling flexibility and encapsulation.

Pro Tip

Use ::part() when outside users need to style your internal component elements. Use ::slotted() when your component needs to style content passed into slots.