Logo "Web Componens" by WebComponents.org (Creative Commons BY-SA 3.0)

Styling Web Components

Permalink

Ich beschäftige mich neuerdings auch mal praktisch mit dem Thema „Web Components„. Dazu habe ich mir ein Projekt mit einem Beispiel aufgesetzt. Nach dem ich mich durch die Grundlagen durchgearbeitet hatte – das Tutorial von Robin Wieruch erschien mir dabei recht nützlich – wollte ich dem Nutzer meiner Komponente die Möglichkeit geben den Style zu beeinflussen. Das erwies sich aber als überraschend wenig intuitive.

Problembeschreibung

Um das Innere einer Web Component zu kapseln verwendet man sogenanntes „Shadow DOM“. So erreicht man das nach außen nur ein einzelnes HTML Element sichtbar ist. Dadurch bedingt kann man aber auch keinen CSS-Selektor mehr erstellen der dieses Shadow DOM adressiert. Daher sehen Web Components immer so aus wie der Entwickler der Komponente es vorgesehen hat. Ein Einfluss von außen ist erst mal nicht vorgesehen. Um ein einheitliches Look and Feel in einer Webanwendung zu erhalten ist aber genau das wünschenswert.

Im Verlauf der Zeit gab es verschiedene Ansätze um eine solche Möglichkeit zu schaffen. Ich beschreibe im folgenden die Ansätze die momentan (also im November 2019) anwendbar sind und halbwegs praktikabel erscheinen. Vorweg: Eine einzelne übergreifend und flexibel nutzbare Lösung habe ich nicht.

Lösungsansatz „Custom Properties

In CSS gibt es mittlerweile eine Art Variablen um konkrete Werte mehrfach in seinem Stylesheet für Eigenschaften setzen zu können. Solche Variablen werden als Custom Properties bezeichnet.

Für die vorliegende Aufgabenstellung sind sie interessant weil sie die Grenzen des Shadow DOMs überwinden. Das bedeutet, außerhalb einer Web Component deklarierte Custom Properties können innerhalb der Web Component genutzt werden um das Shadow DOM zu stylen.

Problem 1: Undeklarierte Variablen

Wenn der Entwickler der Komponente Custom Properties als Werte nutzt und diese dann nicht deklariert wird kommt der angegebene Fallback zum tragen. Wenn ein solcher nicht angegeben wurde wird „unset“ angenommen. Das bedeutet die verschiedenen originalen Styles der Browser werden ausgehebelt.

Am Beispiel eines Buttons wird das Problem deutlich. Dieses Element sieht auf den verschiedenen Plattformen jeweils so aus wie ein Button nun mal auf der jeweiligen Plattform aussieht. Wenn diesem Button nun eine oder mehrere Custom Properties zugewiesen werden legt der Entwickler damit das Aussehen auf genau einen Wert fest: den Wert der Custom Property oder eben unset.

Solange Komponente und die nutzende Seite aus einer Hand kommen kann man noch sicherstellen das alle Custom Properties auch deklariert werden. Sobald aber mehrere Parteien ins Spiel kommen ist das aber schon sehr schwierig.

<style>
  :root {
    --button-background: green;
  }
</style>
<button type="button">
  A button styled by your browser
</button>
<button type="button" style="background: var(--not-declared)">
  A button with background: unset
</button>
<button type="button" style="background: var(--button-background)">
  A button with background: green
</button>   
Beispiel 1: Nutzung undeklarierter Custom Properties

Revert Schlüsselwort

Eine Möglichkeit dieses Problem zu umgehen ist das Keyword „revert“ bei der Zuweisung als Fallback zu setzen. Revert sorgt dafür das die Eigenschaft auf den vom Browser vorgegebenen Wert gesetzt wird. Somit wird also nicht mehr unset angenommen. Das Problem dabei ist allerdings das momentan nur Firefox und Safari das Schlüsselwort unterstützen.

@supports(background:revert) {
   background: var(--button-background, revert);
}
Beispiel 2: Einsatz des Schlüsselworts „revert“

Problem 2: Vielfalt

Schon für ein einzelnes HTML-Element gibt es eine Vielzahl an CSS-Eigenschaften. Für jede davon eine Custom Property vorzusehen ist enorm aufwändig. Bei komplexeren Konstrukten im Shadow DOM wird es schlicht unpraktikabel.

Probleme 3: Pseudoklassen

Noch schlimmer wird es wenn Pseudoklassen wie focus oder hover ins Spiel kommen. Dann müssen auch für diese wiederum Custom Properties vorgesehen werden.

Zusammen betrachtet kann diese Technik nur sehr selektiv eingesetzt werden. Etwas besseres für Firefox ist mir aber noch nicht untergekommen um ein wenig externe Farbe in Web Components zu bringen.

Lösungsansatz part()-Pseudoelement

Goggle hat einen aktuellen Lösungsansatz in Chrome integriert. Das neue Pseudoelement „part“ erlaubt es auf Teile eines Shadow DOMs zuzugreifen. Dazu muss der Entwickler der Komponente das gewünschte Element mittels eines HTML-Attributes das wiederum „part“ heißt mit einem Namen versehen.

<button type="button" part="componentbutton">Ein Button innerhalb eines Shadow DOMs</button>

Anschließend kann der benannte Teil über CSS-Selektoren adressiert werden. So kann auf die übliche Weise das ganze Element neu gestaltet werden. Auch eine Kombination mit Pseudoklassen (fokus, hover) ist im Selektor ebenfalls möglich.

my-component::part(componentbutton) {
   background: green;
}
my-component::part(componentbutton):hover {
   background: darkgreen;
}

Auf diese Weise wird recht elegant ermöglicht das der Komponentenentwickler einzelne Teile seines Shadow DOMs freigeben kann und gleichzeitig erlaubt es dem Anwendungsentwickler eben diese Teile auf dem herkömmlichen Weg seinem Styleguide anzupassen. Leider unterstützten momentan nur Browser auf Chromium basierend dieses Feature. Zumindest Firefox hat allerdings schon eine experimentelle Implementierung. Hier kann man also hoffen.

Historische Lösungsansätze

Es gab seit der Einführung von Web Components auch ein paar andere Ansätze die aber wieder verworfen wurden. Wer ein bisschen „Altertumsforschung“ betreiben will sei der Artikel „Styling Web Components Using A Shared Style Sheet“ zur Recherche empfohlen.

Lizenzangaben

Das Logo das ich zur Illustration dieses Artikels verwende entstammt einer Sammlung von Icons zum Thema Web Components. Es steht unter einer Creative Commons BY-SA 3.0 und wurde durch WebComponents.org bereitgestellt.