Styling im Shadow DOM mit CSS Shadow  Parts  

Avatar of Ollie Williams
Ollie Williams am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Safari 13.1 hat gerade die Unterstützung für CSS Shadow Parts eingeführt. Das bedeutet, dass der Selektor ::part() nun in Chrome, Edge, Opera, Safari und Firefox unterstützt wird. Wir werden sehen, warum er nützlich ist, aber zuerst ein kurzer Rückblick auf die Kapselung des Shadow DOM...

Die Vorteile der Shadow DOM-Kapselung

Ich arbeite bei giffgaff, wo wir eine Vielzahl von CSS-Code haben, der über die Jahre von vielen verschiedenen Leuten auf viele verschiedene Arten geschrieben wurde. Betrachten wir, wie problematisch das sein kann. 

Namenskollisionen

Namenskollisionen zwischen Klassen können in CSS leicht auftreten. Ein Entwickler könnte einen Klassennamen wie `.price` erstellen. Ein anderer Entwickler (oder sogar derselbe) könnte denselben Klassennamen verwenden, ohne es zu wissen.

CSS wird Sie hier nicht auf einen Fehler hinweisen. Nun erhalten alle HTML-Elemente mit dieser Klasse die Formatierung, die für zwei völlig unterschiedliche Dinge vorgesehen ist.

Shadow DOM löst dieses Problem. CSS-in-JS-Bibliotheken wie Emotion und styled-components lösen dieses Problem ebenfalls auf andere Weise, indem sie zufällige Klassennamen generieren, wie z. B. .bwzfXH. Das hilft sicherlich, Konflikte zu vermeiden! CSS-in-JS hindert jedoch niemanden daran, Ihre Komponente auf andere Weise zu beschädigen. Zum Beispiel...

Basisstile und CSS-Resets

Stile können mit HTML-Elementselektoren wie <button> und <div> angewendet werden. Diese Stile könnten eine Komponente beschädigen. Shadow DOM ist das Einzige, was (fast) vollständige Kapselung bietet – Sie können sicher sein, dass Ihre Komponente gleich aussieht, selbst in einer unordentlichen, mit  !important  gespickten Codebasis, da jede Komponente gekapselt ist.

/* This will have no effect on buttons inside shadow DOM */
button { background-color: lime !important; }

Ich würde nicht sagen, dass es gute Praxis ist, Elemente auf diese Weise zu stylen, aber es passiert. Selbst wenn, werden diese Stile keine Auswirkungen auf das Shadow DOM haben.

Es ist erwähnenswert, dass vererbbare Stile wie color, font und line-height in einem Shadow DOM immer noch vererbt werden. Um dies zu verhindern, verwenden Sie all: initial  oder vorzugsweise all: revert, sobald es bessere Browserunterstützung gibt.

Werfen wir einen Blick auf ein gängiges Beispiel für CSS, das direkt auf HTML-Elemente angewendet wird. Betrachten Sie diesen Code aus Eric Meyers Reset

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline;
}

Was ist, wenn die Komponente, mit der wir arbeiten, die Standardwerte des Benutzers für Abstände und Polsterungen nutzt? Dieser Reset könnte dazu führen, dass sie fehlerhaft erscheint, da diese Standardwerte effektiv überschrieben werden.

Shadow DOM ist eine Möglichkeit, diese Probleme zu vermeiden. Shadow DOM ermöglicht es uns, uns voll und ganz darauf zu verlassen, dass eine Komponente wie erwartet gerendert wird, unabhängig davon, in welcher Codebasis sie landet. Ebenso kann keiner der Code, der nur für eine Komponente bestimmt ist, unbeabsichtigt etwas anderes beeinflussen – alles, ohne auf mühsame Konventionen für Klassennamen zurückzugreifen. Shadow DOM bietet ein Maß an Kapselung, das auf keine andere Weise erreicht werden kann.

Kapselung ist großartig, aber wir möchten auch, dass unsere Komponenten thematisch und anpassbar sind. Das wurde mit dem Selektor ::part erheblich erleichtert. 

Shadow DOM mit ::part() stylen

Bisher war der einzige Weg für CSS, das Styling eines benutzerdefinierten Elements außerhalb des Shadow DOM zu ändern, die Verwendung von CSS-Custom-Properties. In einem strengen Designsystem, in dem Sie nur begrenzte Änderungen zulassen möchten, mag das ideal sein. Wenn Sie möchten, dass Ihre Komponente vielseitiger ist, schafft das ein Problem. *Jede* CSS-Eigenschaft, die Sie zum Stylen anbieten möchten, muss über eine benutzerdefinierte Eigenschaft definiert werden. Allein der Klang davon erscheint mühsam.

Die Situation verschärft sich weiter, wenn wir eine Komponente basierend auf Pseudoklassen wie :hover unterschiedlich stylen möchten. Grundsätzlich landen wir bei *unzähligen* benutzerdefinierten Eigenschaften. Betrachten wir ein Beispiel von Ionic, einer Open-Source-Sammlung von Webkomponenten. Schauen Sie sich nur all die benutzerdefinierten Eigenschaften an, die für die Ionic Button-Komponente definiert sind.

Nur zu, ich warte.

Ich habe 23 benutzerdefinierte Eigenschaften gezählt. Unnötig zu sagen, das ist nicht ideal.

Hier ist ein Beispiel, das  ::part() zum Stylen des Elements verwendet. 

In diesem Pen ändere ich einfach die Eigenschaften color, border und background-color, aber ich könnte verwenden, was ich will, ohne durch die definierten benutzerdefinierten Eigenschaften eingeschränkt zu sein. Beachten Sie, dass ich verschiedene *Zustände* des Teils auch mit Pseudoklassen wie :hover und :focus stylen kann.

Die gesamte Komponente in diesem Button-Beispiel wird für das Styling freigegeben, aber wenn Ihre Webkomponente aus mehreren HTML-Elementen besteht, können Sie nur ausgewählte *Teile* der Komponente für diese Art von Styling freigeben – daher der Name ::part. Dies verhindert, dass Benutzer der Komponente beliebige Elemente im Shadow Tree stylen. Es liegt am Autor der Komponente, die Teile der Komponente freizugeben, die er explizit möchte. Andere Teile der Komponente können visuell einheitlich gehalten werden oder benutzerdefinierte Eigenschaften für eine minimalere Anpassbarkeit nutzen. 

Wie richten wir das also für unsere eigenen Komponenten ein? Betrachten wir die Verwendung von ::part, um bestimmte Elemente einer Webkomponente für das Styling bereitzubereiten. Wir fügen einfach ein part-Attribut auf dem Element hinzu, das freigegeben werden soll.

<div part="box">...</div>  
<button>Click me</button>

In diesem Beispiel ist das div mit der vollen Bandbreite an CSS anpassbar – jede CSS-Eigenschaft kann geändert werden. Der Button ist jedoch gesperrt – er kann von niemandem visuell verändert werden, außer vom Autor der Komponente.

Und so wie ein HTML-Element mehrere Klassen haben kann, kann ein Element mehrere Part-Namen haben: 

<div part="box thing">...</div>

Das ist es also, was wir mit ::part bekommen: Indem wir "Teile" eines Elements freigeben, können wir Flexibilität bei der Verwendung einer Webkomponente bieten und gleichzeitig in anderen Bereichen Schutz ausüben. Ob es sich um Ihr Designsystem, eine Komponentenbibliothek oder was auch immer handelt, die Tatsache, dass CSS Shadow Parts Mainstream werden, gibt uns ein weiteres spannendes Werkzeug an die Hand.