Skip to content

CSS Parts in Shadow DOM — Complete Guide

DodaTech Updated 2026-06-28 1 min read

In this tutorial, you will learn about CSS Parts in Shadow Dom. We cover key concepts, practical examples, and best practices to help you master this topic.

CSS parts expose internal shadow elements for external styling via the ::part() pseudo-element, enabling customization without breaking Encapsulation.

What You'll Learn

  • How to define parts with the part attribute
  • How to style parts with ::part()
  • How to use exportparts for nested trees
  • The difference between parts and custom properties

Why It Matters

Custom properties provide theming values, but sometimes you need to target specific elements. CSS parts give controlled access to internal elements.

flowchart LR
  A[Shadow Element] --> B[part=button]
  C[External CSS] --> D[::part(button)]
  D --> B
  B --> E[Styled but encapsulated]

Defining and Styling Parts

class PartDemo extends HTMLElement {
    constructor() {
        super();
        this.attachShadow({ mode: 'open' });
        this.shadowRoot.innerHTML = `
            <style>
                .btn { display: inline-flex; align-items: center; gap: 4px; }
            </style>
            <button part="button" class="btn">
                <span part="icon">\u2605</span>
                <span part="label"><slot></slot></span>
            </button>
        `;
    }
}
customElements.define('part-demo', PartDemo);
/* External styling */
part-demo::part(button) {
    background: #3498db;
    color: white;
    border: none;
    padding: 8px 16px;
    border-radius: 4px;
    cursor: pointer;
}

part-demo::part(button):hover {
    background: #2980b9;
}

part-demo::part(icon) {
    font-size: 1.2em;
}

Exporting Parts from Nested Components

// Inner component defines parts
class InnerComponent extends HTMLElement {
    constructor() {
        super();
        this.attachShadow({ mode: 'open' });
        this.shadowRoot.innerHTML = '<div part="inner-box">Inner Content</div>';
    }
}
customElements.define('inner-component', InnerComponent);

// Outer component exports inner parts
class OuterComponent extends HTMLElement {
    constructor() {
        super();
        this.attachShadow({ mode: 'open' });
        // exportparts: "inner-box: outer-inner-box"
        this.shadowRoot.innerHTML = `
            <inner-component exportparts="inner-box: outer-inner-box"></inner-component>
        `;
    }
}
customElements.define('outer-component', OuterComponent);

// Usage: outer-component::part(outer-inner-box) { background: yellow; }

Common Mistakes

  1. Defining parts on elements that do not need external styling
  2. Not documenting available parts
  3. Forgetting to export nested parts with exportparts

Practice Questions

  1. What attribute exposes an element for styling? The part attribute.
  2. How do nested components expose parts? Using the exportparts attribute.

Mini Project

Create a modal component with parts for overlay, dialog, header, body, footer, close button, and title. Allow users to style each part. Document the parts.

What's Next

Lesson 10: Slot Change Events

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro