Styling von Web Components mit Shadow DOM und CSS
Wenn Sie schon einmal eine Web Component auf einer Seite eingebunden und sich gefragt haben, warum Ihr globales CSS dort plötzlich nicht mehr greift, sind Sie auf die Shadow-Grenze gestoßen. Das ist kein Bug, sondern genau der Sinn der Sache. Shadow DOM verleiht Custom Elements einen eigenen, gekapselten DOM-Baum, was bedeutet, dass Styles standardmäßig weder hinein- noch hinausfließen. Das heißt aber nicht, dass Styling unmöglich wäre — es bedeutet vielmehr, dass Styling bewusst erfolgen muss.
Dieser Artikel behandelt die wichtigsten Mechanismen für das Shadow-DOM-Styling: interne Styles, :host, ::slotted(), CSS Custom Properties, ::part() und adoptedStyleSheets.
Die wichtigsten Erkenntnisse
- Shadow DOM kapselt Styles bewusst ab — globales CSS kann nicht in eine Komponente hineinwirken, und interne Styles können nicht nach außen dringen.
- Shadow-Styles fungieren als Standardwerte und liegen in der Kaskade unterhalb der vom Entwickler verfassten Seiten-Styles, ähnlich wie das User-Agent-Stylesheet eines Browsers.
- Verwenden Sie
:hostund:host(), um das Komponenten-Element selbst zu stylen, und::slotted(), um projizierte Light-DOM-Kindelemente zu gestalten (nur direkte Nachfahren). - CSS Custom Properties und
::part()bilden eine bewusst gestaltete öffentliche Styling-API: Variablen für Theming-Tokens, Parts für strukturelles Styling. adoptedStyleSheetserlaubt es mehreren Komponenten, ein einziges geparstesCSSStyleSheetzu teilen, was die Performance bei großer Skalierung verbessert.
Warum Shadow DOM das Verhalten von CSS verändert
Wenn Sie attachShadow() auf einem Element aufrufen, erzeugen Sie einen separaten DOM-Baum, der an dieses Element gebunden ist (der Shadow Host). Selektoren im Stylesheet Ihrer Seite können nicht in diesen Baum hineinreichen, und Selektoren innerhalb des Shadow-Baums können nicht nach außen wirken.
Eine wichtige Nuance ist dabei zu beachten: Shadow-DOM-Styles stehen in der CSS-Kaskade unterhalb Ihrer eigenen Styles. Das bedeutet, eine globale Regel wie * { color: red } überschreibt eine :host { color: green }-Regel innerhalb des Shadow — selbst wenn die globale Regel früher in der Quellreihenfolge erscheint. Stellen Sie sich Shadow-Styles als die Standard-Styles der Komponente vor, ähnlich wie das User-Agent-Stylesheet eines Browsers Standardwerte für <button> oder <input> bereitstellt.
Den Shadow Host mit :host stylen
Innerhalb des Shadow-Baums selektiert :host das Element, an dem der Shadow angebracht ist:
:host {
display: block;
font-family: sans-serif;
}
Mit :host(selector) können Sie Styles auch bedingt anwenden — abhängig von Attributen oder Klassen am Host-Element:
:host([disabled]) {
opacity: 0.5;
pointer-events: none;
}
Da das Host-Element im Light DOM lebt, überschreiben Styles auf Dokumentebene :host-Regeln. Wenn die Standardwerte der Komponente gewinnen sollen, greifen Sie innerhalb des Shadow zu !important — einer der seltenen Fälle, in denen das wirklich angemessen ist.
Slot-Inhalte mit ::slotted() stylen
Slots ermöglichen es Ihnen, Light-DOM-Inhalte in einen Shadow-Baum zu projizieren. Das Pseudo-Element ::slotted() erlaubt es, diese projizierten Elemente von innerhalb des Shadow zu stylen:
::slotted(p) {
margin: 0;
color: #333;
}
Wichtige Einschränkung: ::slotted() matcht nur das Element, das direkt einem Slot zugewiesen ist — nicht dessen Nachfahren. Ein Selektor wie ::slotted(p span) funktioniert nicht, und ::slotted() akzeptiert ausschließlich einen Compound-Selektor (keine Descendant-Combinators). Für tiefer gehendes Styling verlassen Sie sich auf CSS-Vererbung oder überlassen es den Styles des Light DOM selbst.
CSS Custom Properties überschreiten die Shadow-Grenze
CSS-Variablen (Custom Properties) durchdringen die Shadow-Grenze ungehindert. Das macht sie zum flexibelsten Werkzeug für das Theming von Web Components:
/* In the shadow tree */
:host {
background: var(--card-bg, white);
color: var(--card-color, black);
}
/* In your page stylesheet */
my-card {
--card-bg: #1a1a2e;
--card-color: #eee;
}
Die Komponente definiert die Hooks; der Konsument setzt die Werte. Geben Sie stets Fallback-Werte an, damit die Komponente auch ohne externe Konfiguration funktioniert.
Discover how at OpenReplay.com.
Style-Hooks über CSS Shadow Parts (::part()) freigeben
CSS Shadow Parts sind die moderne Antwort auf das Styling interner Elemente einer Web Component von außen. Innerhalb der Komponente markieren Sie Elemente mit dem part-Attribut:
<button part="trigger">Open</button>
Außerhalb der Komponente können Konsumenten diesen Part direkt über ::part() ansprechen:
my-dialog::part(trigger) {
background: royalblue;
border-radius: 4px;
}
Das ist eine bewusst gestaltete Styling-API — der Autor der Komponente entscheidet, was freigegeben wird. Für strukturelles Styling (Layout, Rahmen, Hintergründe) ist sie mächtiger als CSS-Variablen und hält gleichzeitig interne Implementierungsdetails privat.
Styles effizient teilen mit adoptedStyleSheets
Mit adoptedStyleSheets können Sie CSSStyleSheet-Objekte direkt an eine Shadow Root anhängen. Es wird in modernen Browsern breit unterstützt und eignet sich hervorragend für Komponentenbibliotheken, die ein einmal geparstes Stylesheet über viele Instanzen hinweg teilen müssen:
const sheet = new CSSStyleSheet();
sheet.replaceSync(`:host { display: block; }`);
class MyComponent extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.adoptedStyleSheets = [sheet];
}
}
customElements.define('my-component', MyComponent);
Der Browser parst das Stylesheet nur einmal — unabhängig davon, wie viele Komponenten es verwenden. Das ist ein erheblicher Performance-Gewinn bei großer Skalierung. Sie können Stylesheets hinzufügen oder entfernen, indem Sie das adoptedStyleSheets-Array mutieren, und Änderungen an einem geteilten CSSStyleSheet wirken sich überall dort aus, wo es übernommen wurde.
Den richtigen Ansatz wählen
| Ziel | Werkzeug |
|---|---|
| Das Host-Element der Komponente stylen | :host / :host() |
| Projizierte Light-DOM-Inhalte stylen | ::slotted() |
| Theming-Tokens für Konsumenten bereitstellen | CSS Custom Properties |
| Strukturelles Styling für Konsumenten freigeben | ::part() |
| Styles über viele Komponenten hinweg teilen | adoptedStyleSheets |
Fazit
Beim Styling mit Shadow DOM geht es nicht darum, CSS auszusperren — es geht darum, Kapselung explizit zu machen. Verwenden Sie interne Styles und :host für Standardwerte, CSS-Variablen und ::part() als öffentliche Styling-API und adoptedStyleSheets, wenn Performance bei großer Skalierung zählt. Wenn Sie dieses mentale Modell einmal verinnerlicht haben, wird das Styling von Web Components zur Routine.
FAQs
Weil die Komponente Shadow DOM nutzt, das einen gekapselten DOM-Baum erzeugt, der vom Hauptdokument isoliert ist. Seiten-Stylesheets können keine Elemente innerhalb des Shadow-Baums selektieren. Um Interna zu stylen, muss der Autor der Komponente Hooks bereitstellen — etwa CSS Custom Properties oder CSS Shadow Parts über das part-Attribut, die Sie dann von außen mit ::part() ansprechen.
Verwenden Sie Custom Properties für Design-Tokens wie Farben, Abstände und Schriftarten, die natürlich über Vererbung fließen. Verwenden Sie ::part(), wenn Konsumenten strukturelle Kontrolle über ein bestimmtes internes Element benötigen — etwa um Rahmen, Hintergründe oder das Layout eines Buttons oder Headers zu überschreiben. Parts bieten feinkörnigeren Zugriff, während Variablen einfacher und breiter im Anwendungsbereich bleiben.
Das Pseudo-Element ::slotted() matcht nur Top-Level-Knoten, die direkt einem Slot zugewiesen sind, nicht deren Nachfahren. Es akzeptiert außerdem nur einen Compound-Selektor, sodass Descendant-Combinators ungültig sind. Um Kindelemente von Slot-Inhalten zu stylen, verlassen Sie sich auf die CSS-Vererbung vom Slot-Element oder überlassen es dem Light-DOM-Stylesheet des Konsumenten, diese Nachfahren direkt zu behandeln.
Ja. Es wird in allen modernen Evergreen-Browsern unterstützt und ist der empfohlene Weg, um Styles über viele Shadow Roots hinweg ohne erneutes Parsen zu teilen. Ein einziges geparstes CSSStyleSheet kann an mehrere Shadow Roots angehängt werden, was Speicherverbrauch reduziert und die Startzeit verbessert. Geteilte Stylesheets lassen sich zudem effizient aktualisieren und über mehrere Komponenten hinweg wiederverwenden.
Understand every bug
Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.