::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.