Skip to content

CSS Parts

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

What Are CSS Parts?

CSS Parts allow a Web Component to expose selected internal Shadow DOM elements for external styling using the part attribute and the ::part() selector.

Normally, outside CSS cannot style elements inside Shadow DOM. CSS Parts solve this by letting component authors safely expose only the elements they want consumers to style.

Concept Description
part Attribute used inside Shadow DOM to expose an element
::part() CSS selector used outside the component to style exposed parts
Shadow DOM Styling Allows controlled styling of internal elements
Main Benefit Customize component UI without breaking encapsulation
Common Use Buttons, labels, headers, icons, cards, inputs, and footers

Basic CSS Parts 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 with part="button". Outside CSS can style it using app-button::part(button).

CSS Parts in Web Components

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

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

    this.shadowRoot.innerHTML = `
      <article part="card">
        <header part="header">
          <h2 part="title"><slot name="title">User</slot></h2>
        </header>

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

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

customElements.define("user-card", UserCard);
user-card::part(card) {
  border: 1px solid #dee2e6;
  border-radius: 1rem;
  padding: 1rem;
  background: #ffffff;
}

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

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

Each exposed part acts like a public styling hook for the component.

How CSS Parts Work

Step What Happens
1. Create Shadow DOM The component keeps internal markup private
2. Add part Expose selected internal elements
3. Use ::part() Style exposed elements from outside CSS
4. Keep privacy Only named parts are styleable
5. Build styling API Consumers get safe customization points

Using Multiple Part Names

<button part="button primary">
  Save
</button>
app-button::part(button) {
  padding: 0.5rem 1rem;
}

app-button::part(primary) {
  background: #0d6efd;
  color: white;
}

One element can have multiple part names. This helps expose general and variant-specific styling hooks.

CSS Parts vs Internal Classes

Feature Internal Classes CSS Parts
Outside Styling Cannot reliably style inside Shadow DOM Can style exposed Shadow DOM elements
Encapsulation Private implementation detail Public styling API
Recommended For Internal component styling Consumer customization
Example .button app-button::part(button)

CSS Parts vs CSS Custom Properties

Feature CSS Parts CSS Custom Properties
Best For Styling exposed internal elements Design tokens like colors and spacing
API Style Selector-based styling Variable-based styling
Example app-card::part(title) --card-title-color
Control Level More direct styling control More controlled design values
Use Together? Yes Yes

Form Input Component With CSS Parts

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

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

    this.shadowRoot.innerHTML = `
      <label part="label">
        <slot name="label">Label</slot>
      </label>

      <input part="input" type="text" />

      <p part="message">
        <slot name="message"></slot>
      </p>
    `;
  }
}

customElements.define("app-input", AppInput);
app-input::part(label) {
  display: block;
  font-weight: 600;
  margin-bottom: 0.25rem;
}

app-input::part(input) {
  border: 1px solid #ced4da;
  border-radius: 0.5rem;
  padding: 0.5rem;
}

app-input::part(message) {
  color: #dc3545;
  font-size: 0.875rem;
}

This example exposes only the label, input, and message areas. Other internal implementation details remain private.

When to Use CSS Parts

  • You want to style specific elements inside Shadow DOM.
  • You are building reusable Web Components.
  • You want to expose safe styling hooks to consumers.
  • You need more control than CSS Custom Properties provide.
  • You want to keep internal class names private.
  • You are creating design system components.

Common CSS Parts Use Cases

  • Styling a button inside a custom button component.
  • Styling the header, body, and footer of a card component.
  • Styling labels and inputs inside form components.
  • Styling icons inside menu or tab components.
  • Styling modal header, content, and close button.
  • Styling exposed table rows, cells, or toolbar areas.

CSS Parts Best Practices

  • Expose only the parts consumers really need to style.
  • Use clear names like button, title, header, and footer.
  • Treat part names as a public API.
  • Avoid changing part names often because it can break consumer CSS.
  • Use CSS Custom Properties for tokens and CSS Parts for element-level styling.
  • Document all supported parts for each component.
  • Keep internal classes private and use part for public styling hooks.

Common CSS Parts Mistakes

  • Trying to style Shadow DOM elements with normal outside CSS selectors.
  • Forgetting to add the part attribute inside Shadow DOM.
  • Using unclear part names like box1 or item2.
  • Exposing too many internal elements as parts.
  • Changing part names without considering backward compatibility.
  • Using CSS Parts for simple values that should be CSS Custom Properties.
  • Not documenting the public styling API.

Key Takeaways

  • CSS Parts expose selected Shadow DOM elements for styling.
  • Use the part attribute inside the component.
  • Use the ::part() selector outside the component.
  • CSS Parts keep Shadow DOM private while allowing safe customization.
  • They are useful for design systems and reusable Web Components.

Pro Tip

Use CSS Custom Properties for theme values like colors, spacing, and radius. Use CSS Parts when consumers need to style a specific internal element such as a button, title, icon, or footer.