CSS Shadow Parts

Master the art of styling Shadow DOM elements with the ::part() pseudo-element. Learn how component authors expose customizable elements and how consumers apply external styles.

Understanding Shadow DOM and the Need for Parts

Shadow DOM is a web standard that enables encapsulation of HTML, CSS, and JavaScript within a component. When you create a custom element using Shadow DOM, the styles and markup defined inside the shadow tree are isolated from the rest of the document. This isolation prevents style conflicts--for example, a global button class in your main stylesheet won't accidentally affect buttons inside a web component, and component-specific styles won't leak out to affect other elements on the page.

However, this encapsulation creates a challenge: if all styles are isolated, how can developers customize the appearance of internal elements within a component? This is precisely the problem that CSS Shadow Parts was designed to solve. Rather than completely breaking encapsulation, Shadow Parts provides a deliberate, opt-in mechanism for exposing specific elements that component authors want to be customizable.

The Evolution of Component Styling

Before Shadow Parts existed, developers had limited options for styling web component internals. They could use CSS custom properties (variables) that penetrate through the shadow boundary, or they could use JavaScript to manipulate styles at runtime. While CSS custom properties remain a valuable tool for theming and simple customizations, they have limitations in terms of what properties can be controlled and how specific the targeting can be. Shadow Parts complements these approaches by enabling full CSS selector-based styling of explicitly exposed elements, providing significantly more flexibility for component consumers.

How CSS Shadow Parts Work

The CSS Shadow Parts specification defines two key components: the part HTML attribute and the ::part() CSS pseudo-element. Component authors add the part attribute to elements inside their shadow tree that they want to expose for external styling. Consumers of the component can then target these exposed elements using the ::part() pseudo-element in their CSS, selecting them by the name specified in the part attribute.

For teams building modern web applications with custom elements, mastering Shadow Parts is essential for creating flexible, reusable component libraries that balance encapsulation with customization.

Exposing Parts in a Web Component
1<template id="custom-card">2 <style>3 .card {4 border: 1px solid #e0e0e0;5 border-radius: 8px;6 padding: 16px;7 }8 .header {9 font-size: 1.25rem;10 font-weight: bold;11 margin-bottom: 12px;12 }13 .content {14 color: #424242;15 line-height: 1.5;16 }17 </style>18 <div class="card">19 <div part="card-header" class="header">20 <slot name="title"></slot>21 </div>22 <div part="card-content" class="content">23 <slot></slot>24 </div>25 </div>26</template>

The part Attribute: Exposing Elements

The part attribute is a global HTML attribute that can be applied to any element within a shadow tree. When an element has a part attribute, it becomes visible to the outside DOM through the ::part() pseudo-element. The attribute accepts one or more part names, separated by spaces, allowing an element to expose multiple parts if needed.

Multiple Parts on a Single Element

An element can expose multiple parts by including multiple names in the part attribute, separated by spaces. This is useful when an element serves multiple semantic purposes or when you want to enable separate styling contexts for different aspects of the same element:

<div part="tab active" class="tab-item">Tab A</div>

Exporting Parts in Nested Shadow Trees

When shadow trees are nested--meaning a shadow host contains a component that itself uses Shadow DOM--parts are not automatically visible to all ancestors. Each shadow boundary is a separate encapsulation context. To make parts from nested shadow trees visible to outer contexts, the exportparts attribute must be used. This attribute explicitly forwards parts from an inner shadow tree to make them available to outer styling contexts.

Web components built with proper Shadow Parts integration work seamlessly with AI-powered development workflows, enabling intelligent customization and theming capabilities.

Key Capabilities

CSS Shadow Parts enables powerful styling patterns for web components

Pseudo-Class Support

Combine ::part() with :hover, :focus, and other pseudo-classes for interactive styling

Pseudo-Element Chaining

Style ::before, ::after, and ::first-letter on exposed parts

Multiple Part Names

An element can expose multiple parts for flexible targeting

Nested Component Support

Use exportparts to forward parts through nested shadow boundaries

Practical Example: Custom Card Component

Here's how to create a complete custom element that uses Shadow Parts to expose customizable elements. This example demonstrates best practices for designing component APIs with parts.

JavaScript Component Definition

class CustomCard extends HTMLElement {
 constructor() {
 super();
 this.attachShadow({ mode: 'open' });
 }

 connectedCallback() {
 this.render();
 }

 render() {
 this.shadowRoot.innerHTML = `
 <style>
 :host {
 display: block;
 --card-border: 1px solid #e0e0e0;
 --card-radius: 8px;
 }

 .card {
 border: var(--card-border);
 border-radius: var(--card-radius);
 padding: 16px;
 background: white;
 }
 </style>

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

 <div part="card-body" class="body">
 <slot></slot>
 </div>

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

customElements.define('custom-card', CustomCard);

Consumer Styling with ::part()

/* Style the header */
custom-card::part(card-header) {
 font-size: 1.25rem;
 font-weight: 600;
 color: #333;
}

/* Style the title specifically */
custom-card::part(card-title) {
 text-transform: uppercase;
 letter-spacing: 0.05em;
}

/* Interactive states */
custom-card::part(card-footer):hover {
 color: #333;
}

Combining with Pseudo-Classes

One of the powerful features of ::part() is its ability to be combined with pseudo-classes for interactive and state-based styling:

custom-card::part(card-header):hover {
 color: #1565c0;
 background-color: #f5f5f5;
}

custom-card::part(card-content):focus {
 outline: 2px solid #2196f3;
}

Best Practices for Component Authors

Expose Only What You Mean to Expose

When designing a component with Shadow Parts, be intentional about which elements you expose. Parts should be semantically meaningful and stable--they represent a contract between the component author and consumers. Avoid exposing implementation details that might change or that consumers shouldn't need to customize.

Use Descriptive Part Names

Part names should be descriptive and follow consistent naming conventions across your component library. Consider using hyphenated multi-word names for clarity:

  • card-header
  • action-button
  • tab-content

Provide Documentation

Every component that exposes parts should clearly document which parts are available and what elements they correspond to. Include examples of common customizations and note any styling limitations.

Combine with CSS Custom Properties

While Shadow Parts provide powerful styling capabilities, CSS custom properties (variables) remain valuable for theming and simple customizations. Consider providing both mechanisms:

  • Parts for structural and layout customizations
  • CSS custom properties for theming values

Performance Considerations

Shadow DOM doesn't inherently impact rendering performance in a negative way. Modern browsers are well-optimized for Shadow DOM rendering, and the encapsulation can actually improve performance by reducing style recalculation scope. The ::part() selector follows the same performance characteristics as other CSS selectors, and modern browser engines optimize selector matching effectively.

For teams implementing modern web applications, combining web components with comprehensive SEO strategies ensures that even highly encapsulated custom elements remain discoverable and optimized for search engines.

Browser Compatibility

CSS Shadow Parts has been widely supported since 2020 and is considered a Baseline feature, meaning it works across all modern browsers including Chrome, Firefox, Safari, and Edge. The specification reached W3C Recommendation status, ensuring stability and interoperability.

Browser Support for CSS Shadow Parts
BrowserVersionStatus
Chrome89+Supported
Firefox72+Supported
Safari13.1+Supported
Edge89+Supported
Safari iOS13.4+Supported

Frequently Asked Questions

Build Customizable Web Components

Master Shadow Parts to create flexible, reusable components that give consumers the styling control they need.