Shadow DOM Styling — Complete Guide
In this tutorial, you will learn about Shadow Dom Styling. We cover key concepts, practical examples, and best practices to help you master this topic.
Shadow DOM styling techniques include :host, :host-context, ::slotted, CSS custom properties, and CSS parts for flexible component customization.
What You'll Learn
- How to use :host and :host-context for host element styling
- How to style slotted content with ::slotted
- How CSS custom properties cross the shadow boundary
- How CSS parts enable targeted external styling
Why It Matters
Encapsulation is powerful but creates a problem: how do users customize component appearance? CSS custom properties and parts provide controlled customization points.
flowchart LR A[Shadow Styles] --> B[:host] A --> C[::slotted] A --> D[CSS Custom Properties] A --> E[CSS Parts] B --> F[Style host element] C --> G[Style projected content] D --> H[Cross-boundary theming] E --> I[Exposed styling hooks]
::slotted Styling
class StyledList extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
.list { border: 1px solid #ddd; border-radius: 4px; padding: 8px; }
::slotted(*) { padding: 8px; border-bottom: 1px solid #eee; display: block; }
::slotted(.highlight) { background: #fff3cd; }
::slotted([slot="header"]) { font-weight: bold; font-size: 1.1em; background: #f5f5f5; }
</style>
<div class="list">
<slot name="header"></slot>
<slot></slot>
</div>
`;
}
}
customElements.define('styled-list', StyledList);
CSS Custom Properties for Theming
class ThemeCard extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
:host {
--card-bg: #ffffff;
--card-border: #ddd;
--card-title-color: #333;
--card-text-color: #666;
--card-radius: 8px;
}
.card {
background: var(--card-bg);
border: 1px solid var(--card-border);
border-radius: var(--card-radius);
padding: 16px;
}
.title { color: var(--card-title-color); font-weight: bold; }
.body { color: var(--card-text-color); }
</style>
<div class="card">
<div class="title"><slot name="title"></slot></div>
<div class="body"><slot></slot></div>
</div>
`;
}
}
customElements.define('theme-card', ThemeCard);
Expected output: Users customize via CSS: theme-card { --card-bg: #f0f8ff; --card-border: #4682b4; }
CSS Parts
class PartButton extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
button { padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer; }
.icon { margin-right: 4px; }
</style>
<button part="button">
<span part="icon" class="icon"></span>
<slot></slot>
</button>
`;
}
}
customElements.define('part-button', PartButton);
Expected output: External CSS targets parts: part-button::part(button) { background: red; }
Common Mistakes
- Using ::slotted with complex selectors (only simple selectors work)
- Forgetting that ::slotted styles the slotted element, not its children
- Not providing CSS custom property defaults with var()
Practice Questions
- What does ::slotted(div) style? The slotted div element itself, not its descendants.
- How do CSS custom properties cross the shadow boundary? They inherit into the shadow tree from the host element.
Mini Project
Create a component library with consistent theming via CSS custom properties. Include button, card, and badge components. Each component exposes CSS parts for targeted customization.
What's Next
Lesson 8: Shadow DOM Slots
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro