Gestalten von Webkomponenten mithilfe eines gemeinsam genutzten Stylesheets
Veröffentlicht: 2022-03-10Webkomponenten sind eine erstaunliche neue Funktion des Webs, die es Entwicklern ermöglicht, ihre eigenen benutzerdefinierten HTML-Elemente zu definieren. In Kombination mit einem Styleguide können Webkomponenten eine Komponenten-API erstellen, die es Entwicklern ermöglicht, das Kopieren und Einfügen von Codeausschnitten zu beenden und stattdessen einfach ein DOM-Element zu verwenden. Durch die Verwendung des Schatten-DOM können wir die Webkomponente kapseln und müssen uns keine Gedanken über Spezifitätskriege mit anderen Stylesheets auf der Seite machen.
Allerdings scheinen Web-Komponenten und Styleguides derzeit widersprüchlich zu sein. Einerseits bieten Styleguides eine Reihe von Regeln und Stilen, die global auf die Seite angewendet werden und die Konsistenz auf der gesamten Website gewährleisten. Andererseits verhindern Webkomponenten mit dem Schatten-DOM, dass globale Stile ihre Kapselung durchdringen, wodurch verhindert wird, dass der Styleguide sie beeinflusst.
Weiterführende Literatur zu SmashingMag:
- Best Practices in komponentenbasierten Systemen durchsetzen
- So verwenden Sie den LESS CSS-Präprozessor für intelligentere Stylesheets
- Ein tiefer Einblick in Adobe Edge Reflow
Wie können die beiden also koexistieren, wobei globale Styleguides weiterhin Konsistenz und Stile bieten, sogar für Webkomponenten mit dem Schatten-DOM? Glücklicherweise gibt es Lösungen, die heute funktionieren, und es werden noch weitere Lösungen kommen, die es globalen Styleguides ermöglichen, Webkomponenten ein Styling zu verleihen. (Für den Rest dieses Artikels werde ich den Begriff „Webkomponenten“ verwenden, um mich auf benutzerdefinierte Elemente mit dem Schatten-DOM zu beziehen.)
Welchen Stil sollte ein globaler Styleguide in einer Webkomponente haben?
Bevor wir diskutieren, wie man einen globalen Styleguide zum Stylen einer Webkomponente erhält, sollten wir diskutieren, was es zu stylen versuchen sollte und was nicht.
Zunächst einmal besagen aktuelle Best Practices für Webkomponenten, dass eine Webkomponente, einschließlich ihrer Stile, gekapselt werden sollte, damit sie nicht von externen Ressourcen abhängig ist, um zu funktionieren. Dadurch kann es überall auf oder außerhalb der Website verwendet werden, auch wenn der Styleguide nicht verfügbar ist.
Unten sehen Sie eine einfache Webkomponente für ein Anmeldeformular, die alle ihre Stile enthält.
<template> <style> :host { color: #333333; font: 16px Arial, sans-serif; } p { margin: 0; } p + p { margin-top: 20px; } a { color: #1f66e5; } label { display: block; margin-bottom: 5px; } input[type="text"], input[type="password"] { background-color: #eaeaea; border: 1px solid grey; border-radius: 4px; box-sizing: border-box; color: inherit; font: inherit; padding: 10px 10px; width: 100%; } input[type="submit"] { font: 16px/1.6 Arial, sans-serif; color: white; background: cornflowerblue; border: 1px solid #1f66e5; border-radius: 4px; padding: 10px 10px; width: 100%; } .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <div class="container"> <form action="#"> <p> <label for="username">User Name</label> <input type="text" name="username"> </p> <p> <label for="password">Password</label> <input type="password" name="password"> </p> <p> <input type="submit" value="Login"> </p> <p class="footnote">Not registered? <a href="#">Create an account</a></p> </form> </div> </template> <script> const doc = (document._currentScript || document.currentScript).ownerDocument; const template = doc.querySelector('#login-form-template'); customElements.define('login-form', class extends HTMLElement { constructor() { super(); const root = this.attachShadow({mode: 'closed'}); const temp = document.importNode(template.content, true); root.appendChild(temp); } }); </script>
Hinweis: Codebeispiele sind in der Version 1-Spezifikation für Webkomponenten geschrieben.

Die vollständige Kapselung aller Webkomponenten würde jedoch unweigerlich zu vielen doppelten CSS führen, insbesondere wenn es darum geht, die Typografie und das Styling nativer Elemente einzurichten. Wenn ein Entwickler einen Absatz, ein Anker-Tag oder ein Eingabefeld in seiner Webkomponente verwenden möchte, sollte es wie der Rest der Website gestylt sein.
Wenn wir alle Stile, die die Webkomponente benötigt, vollständig kapseln, würde das CSS für die Gestaltung von Absätzen, Anker-Tags, Eingabefeldern usw. in allen Webkomponenten dupliziert, die sie verwenden. Dies würde nicht nur die Wartungskosten erhöhen, sondern auch zu viel größeren Download-Größen für die Benutzer führen.
Anstatt alle Stile zu kapseln, sollten Webkomponenten nur ihre einzigartigen Stile kapseln und sich dann auf einen Satz gemeinsam genutzter Stile verlassen, um Stile für alles andere zu handhaben. Diese gemeinsam genutzten Stile würden im Wesentlichen zu einer Art Normalize.css werden, die Webkomponenten verwenden könnten, um sicherzustellen, dass native Elemente gemäß dem Styleguide gestylt werden.
Im vorherigen Beispiel würde die Webkomponente für das Anmeldeformular die Stile nur für ihre beiden eindeutigen Klassen deklarieren: .container
und .footnote
. Der Rest der Stile würde in das gemeinsam genutzte Stylesheet gehören und die Absätze, Anker-Tags, Eingabefelder usw. formatieren.
Kurz gesagt, der Styleguide sollte nicht versuchen, die Webkomponente zu stylen, sondern stattdessen eine Reihe gemeinsamer Stile bereitstellen, die Webkomponenten verwenden können, um ein konsistentes Erscheinungsbild zu erzielen.
So gestaltete man früher das Shadow DOM mit externen Stylesheets
Die anfängliche Spezifikation für Webkomponenten (bekannt als Version 0) ermöglichte es jedem externen Stylesheet, in das Shadow-DOM einzudringen, indem die CSS-Selektoren ::shadow
oder /deep/
verwendet wurden. Die Verwendung von ::shadow
und /deep/
ermöglichte es Ihnen, einen Styleguide in das Shadow-DOM einzubinden und die gemeinsam genutzten Styles einzurichten, unabhängig davon, ob die Webkomponente dies wollte oder nicht.
/* Style all p tags inside a web components shadow DOM */ login-form::shadow p { color: red; }
Mit dem Erscheinen der neuesten Version der Webkomponentenspezifikation (bekannt als Version 1) haben die Autoren die Fähigkeit externer Stylesheets entfernt, in das Schatten-DOM einzudringen, und sie haben keine Alternative bereitgestellt. Stattdessen hat sich die Philosophie von der Verwendung von Drachen zur Gestaltung von Webkomponenten zur Verwendung von Brücken geändert. Mit anderen Worten, die Autoren von Webkomponenten sollten dafür verantwortlich sein, welche externen Stilregeln ihre Komponente formatieren dürfen, anstatt gezwungen zu sein, sie zuzulassen.
Leider hat diese Philosophie das Internet noch nicht wirklich eingeholt, was uns etwas in Bedrängnis bringt. Glücklicherweise ermöglichen einige heute verfügbare und einige in nicht allzu ferner Zukunft kommende Lösungen ein gemeinsam genutztes Stylesheet zum Gestalten einer Webkomponente.
Was Sie heute tun können
Es gibt drei Techniken, die Sie heute verwenden können, um es einer Webkomponente zu ermöglichen, Stile gemeinsam zu nutzen: @import
, benutzerdefinierte Elemente und eine Webkomponentenbibliothek.
Mit @import
Die einzige native Möglichkeit, ein Stylesheet heute in eine Webkomponente einzufügen, ist die Verwendung von @import
. Obwohl dies funktioniert, ist es ein Anti-Pattern. Für Webkomponenten ist dies jedoch ein noch größeres Leistungsproblem.
<template> <style> @import "styleguide.css" </style> <style> .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- rest of template DOM --> </template>
Normalerweise ist @import
ein Anti-Pattern, da es alle Stylesheets in Serie herunterlädt, statt parallel, besonders wenn sie verschachtelt sind. In unserer Situation kann dem Herunterladen eines einzelnen Stylesheets in Serie nicht geholfen werden, also sollte es theoretisch in Ordnung sein. Aber als ich dies in Chrome testete, zeigten die Ergebnisse, dass die Verwendung von @import
dazu führte, dass die Seite bis zu einer halben Sekunde langsamer gerendert wurde, als wenn die Stile nur direkt in die Webkomponente eingebettet würden.
Hinweis: Aufgrund von Unterschieden in der Funktionsweise des Polyfill von HTML-Importen im Vergleich zu nativen HTML-Importen kann WebPagetest.org nur verwendet werden, um zuverlässige Ergebnisse in Browsern zu liefern, die HTML-Importe nativ unterstützen (z. B. Chrome).

@import
dazu führt, dass der Browser bis zu einer halben Sekunde langsamer rendert als beim direkten Einbetten der Stile in die Webkomponente. Am Ende ist @import
immer noch ein Anti-Pattern und kann ein Performance-Problem in Webkomponenten sein. Also keine tolle Lösung.
Verwenden Sie nicht das Shadow-DOM
Da das Problem beim Versuch, gemeinsame Stile für Webkomponenten bereitzustellen, auf die Verwendung des Schatten-DOM zurückzuführen ist, besteht eine Möglichkeit, das Problem vollständig zu vermeiden, darin, das Schatten-DOM nicht zu verwenden.
Wenn Sie das Schatten-DOM nicht verwenden, erstellen Sie benutzerdefinierte Elemente anstelle von Webkomponenten (siehe nebenstehende Seite), wobei der einzige Unterschied darin besteht, dass das Schatten-DOM und der Geltungsbereich fehlen. Ihr Element wird den Stilen der Seite unterliegen, aber damit müssen wir uns heute schon befassen, also ist es nichts, womit wir nicht schon umgehen können. Benutzerdefinierte Elemente werden vollständig von webcomponentjs polyfill unterstützt, das eine hervorragende Browserunterstützung bietet.
Der größte Vorteil von benutzerdefinierten Elementen besteht darin, dass Sie mit ihnen heute eine Musterbibliothek erstellen können und nicht warten müssen, bis das Problem des gemeinsamen Stylings gelöst ist. Und da der einzige Unterschied zwischen Webkomponenten und benutzerdefinierten Elementen das Schatten-DOM ist, können Sie das Schatten-DOM jederzeit in Ihren benutzerdefinierten Elementen aktivieren, sobald eine Lösung für gemeinsames Styling verfügbar ist.
Wenn Sie benutzerdefinierte Elemente erstellen möchten, beachten Sie einige Unterschiede zwischen benutzerdefinierten Elementen und Webkomponenten.
Erstens sollten Sie sicherstellen, dass Ihre Selektoren keine Konflikte verursachen, da Stile für das benutzerdefinierte Element den Seitenstilen unterliegen und umgekehrt. Wenn Ihre Seiten bereits einen Styleguide verwenden, lassen Sie die Stile für das benutzerdefinierte Element im Styleguide und lassen Sie das Element das erwartete DOM und die Klassenstruktur ausgeben.
Indem Sie die Stile im Styleguide belassen, erstellen Sie einen reibungslosen Migrationspfad für Ihre Entwickler, da sie den Styleguide weiterhin wie zuvor verwenden können, aber dann langsam zur Verwendung des neuen benutzerdefinierten Elements migrieren, wenn sie dazu in der Lage sind. Sobald jeder das benutzerdefinierte Element verwendet, können Sie die Stile in das Element verschieben, um sie zusammenzuhalten und später eine einfachere Umgestaltung in Webkomponenten zu ermöglichen.
Stellen Sie zweitens sicher, dass Sie JavaScript-Code in einen sofort aufgerufenen Funktionsausdruck (IFFE) kapseln, damit Sie keine Variablen in den globalen Geltungsbereich einfließen lassen. Benutzerdefinierte Elemente bieten nicht nur keinen CSS-Bereich, sondern auch keinen JavaScript-Bereich.
Drittens müssen Sie die connectedCallback
Funktion des benutzerdefinierten Elements verwenden, um das Vorlagen-DOM zum Element hinzuzufügen. Gemäß der Webkomponentenspezifikation sollten benutzerdefinierte Elemente während der Konstruktorfunktion keine untergeordneten Elemente hinzufügen, daher müssen Sie das Hinzufügen des DOM zur connectedCallback
-Funktion verschieben.
Schließlich funktioniert das <slot>
-Element nicht außerhalb des Schatten-DOM. Das bedeutet, dass Sie eine andere Methode verwenden müssen, um Entwicklern die Möglichkeit zu geben, ihre Inhalte in Ihr benutzerdefiniertes Element einzufügen. Normalerweise bedeutet dies, dass Sie das DOM einfach selbst manipulieren, um den Inhalt dort einzufügen, wo Sie ihn haben möchten.
Da es jedoch bei benutzerdefinierten Elementen keine Trennung zwischen dem Schatten-DOM und dem Licht-DOM gibt, müssen Sie aufgrund der kaskadierenden Stile Ihrer Elemente auch sehr darauf achten, das eingefügte DOM nicht zu formatieren.
<!-- login-form.html --> <template> <style> login-form .container { max-width: 300px; padding: 50px; border: 1px solid grey; } login-form .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template> <script> (function() { const doc = (document._currentScript || document.currentScript).ownerDocument; const template = doc.querySelector('#login-form-template'); customElements.define('login-form', class extends HTMLElement { constructor() { super(); } // Without the shadow DOM, we have to manipulate the custom element // after it has been inserted in the DOM. connectedCallback() { const temp = document.importNode(template.content, true); this.appendChild(temp); } }); })(); </script>
<!-- index.html --> <link rel="stylesheet" href="styleguide.css"> <link rel="import" href="login-form.html"> <login-form></login-form>
Benutzerdefinierte Elemente sind in Bezug auf die Leistung fast so schnell wie Webkomponenten, die nicht verwendet werden (dh das gemeinsame Stylesheet im head
verknüpfen und nur native DOM-Elemente verwenden). Von allen Techniken, die Sie heute verwenden können, ist dies bei weitem die schnellste.

Beiseite: Ein benutzerdefiniertes Element ist immer noch eine Webkomponente für alle Absichten und Zwecke. Der Begriff „Webkomponenten“ wird verwendet, um vier separate Technologien zu beschreiben: benutzerdefinierte Elemente, Vorlagen-Tags, HTML-Importe und das Schatten-DOM.
Leider wurde der Begriff verwendet, um alles zu beschreiben, was eine beliebige Kombination der vier Technologien verwendet. Dies hat zu viel Verwirrung darüber geführt, was die Leute meinen, wenn sie „Webkomponente“ sagen. Wie Rob Dodson herausfand, fand ich es hilfreich, unterschiedliche Begriffe zu verwenden, wenn es um benutzerdefinierte Elemente mit und ohne Schatten-DOM ging.
Die meisten Entwickler, mit denen ich gesprochen habe, neigen dazu, den Begriff „Webkomponente“ mit einem benutzerdefinierten Element zu assoziieren, das das Schatten-DOM verwendet. Für die Zwecke dieses Artikels habe ich also eine künstliche Unterscheidung zwischen einer Webkomponente und einem benutzerdefinierten Element erstellt.
Verwenden einer Webkomponentenbibliothek
Eine andere Lösung, die Sie heute verwenden können, ist eine Webkomponentenbibliothek wie Polymer, SkateJS oder X-Tag. Diese Bibliotheken helfen, die Lücken des heutigen Supports zu schließen, und können auch den Code vereinfachen, der zum Erstellen einer Webkomponente erforderlich ist. Sie bieten normalerweise auch zusätzliche Funktionen, die das Schreiben von Webkomponenten erleichtern.
Mit Polymer können Sie beispielsweise eine einfache Webkomponente in nur wenigen JavaScript-Zeilen erstellen. Ein zusätzlicher Vorteil besteht darin, dass Polymer eine Lösung für die Verwendung des Schatten-DOM und eines gemeinsam genutzten Stylesheets bereitstellt. Das bedeutet, dass Sie heute Webkomponenten erstellen können, die Stile gemeinsam nutzen.
Erstellen Sie dazu ein sogenanntes Stilmodul, das alle gemeinsam genutzten Stile enthält. Es kann sich entweder um ein <style>
-Tag handeln, in das die gemeinsam genutzten Stile eingebettet sind, oder um ein <link rel=“import”>
Tag, das auf ein gemeinsam genutztes Stylesheet verweist. Schließen Sie in beiden Fällen die Stile mit einem <style include>
-Tag in Ihre Webkomponente ein, und Polymer parst die Stile und fügt sie Ihrer Webkomponente als Inline- <style>
-Tag hinzu.

<!-- shared-styles.html --> <dom-module> <!-- Link to a shared style sheet --> <!-- <link rel="import" href="styleguide.css"> --> <!-- Inline the shared styles --> <template> <style> :host { color: #333333; font: 16px Arial, sans-serif; } /* Rest of shared CSS */ </style> </template> </dom-module>
<!-- login-form.html --> <link rel="import" href="../polymer/polymer.html"> <link rel="import" href="../shared-styles/shared-styles.html"> <dom-module> <template> <!-- Include the shared styles --> <style include="shared-styles"></style> <style> .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template> <script> Polymer({ is: 'login-form' }); </script> </dom-module>
Der einzige Nachteil bei der Verwendung einer Bibliothek besteht darin, dass sie die Renderzeit Ihrer Webkomponenten verzögern kann. Dies sollte nicht überraschen, da das Herunterladen und Verarbeiten des Codes der Bibliothek einige Zeit in Anspruch nimmt. Alle Webkomponenten auf der Seite können nicht mit dem Rendern beginnen, bis die Bibliothek die Verarbeitung abgeschlossen hat.
Im Fall von Polymer kann es die Seitenrenderzeit im Vergleich zu nativen Webkomponenten um bis zu einer halben Sekunde verzögern. Ein Stilmodul, das die Stile einbettet, ist etwas langsamer als ein Stilmodul, das die Stile verknüpft, und das direkte Einbetten der Stile in die Webkomponente ist genauso schnell wie die Verwendung eines Stilmoduls.
Auch hier tut Polymer nichts Besonderes, um die Renderzeit zu verlangsamen. Das Herunterladen der Polymer-Bibliothek und das Verarbeiten all ihrer großartigen Funktionen sowie das Erstellen aller Vorlagenbindungen nehmen Zeit in Anspruch. Es ist nur der Kompromiss, den Sie eingehen müssen, um eine Webkomponentenbibliothek zu verwenden.

Ergebnisse von Leistungstests zeigen, dass Webkomponenten mit Polymer bis zu einer halben Sekunde langsamer rendern als native Webkomponenten.
Das Versprechen der Zukunft
Wenn keine der aktuellen Lösungen für Sie funktioniert, verzweifeln Sie nicht. Wenn alles gut geht, werden wir innerhalb von ein paar Monaten bis ein paar Jahren in der Lage sein, gemeinsame Stile mit ein paar verschiedenen Ansätzen zu verwenden.
Benutzerdefinierte Eigenschaften
Benutzerdefinierte Eigenschaften (oder CSS-Variablen, wie sie genannt wurden) sind eine Möglichkeit, Variablen in CSS festzulegen und zu verwenden. Dieser Begriff ist für CSS-Präprozessoren nicht neu, aber als native CSS-Funktion sind benutzerdefinierte Eigenschaften tatsächlich leistungsfähiger als eine Präprozessorvariable.
Verwenden Sie zum Deklarieren einer benutzerdefinierten Eigenschaft die Notation für benutzerdefinierte Eigenschaften –my-variable: value
, und greifen Sie mit property: var(–my-variable)
auf die Variable zu. Eine benutzerdefinierte Eigenschaft wird wie jede andere CSS-Regel kaskadiert, sodass ihr Wert von ihrer übergeordneten Eigenschaft erbt und überschrieben werden kann. Der einzige Vorbehalt bei benutzerdefinierten Eigenschaften besteht darin, dass sie innerhalb eines Selektors deklariert werden müssen und im Gegensatz zu Präprozessorvariablen nicht eigenständig deklariert werden können.
<style> /* Declare the custom property */ html { --main-bg-color: red; } /* Use the custom property */ input { background: var(--main-bg-color); } </style>
Eine Sache, die benutzerdefinierte Eigenschaften so mächtig macht, ist ihre Fähigkeit, das Schatten-DOM zu durchdringen. Dies ist nicht die gleiche Idee wie die Selektoren /deep/
und ::shadow
, da sie sich nicht in die Webkomponente drängen. Stattdessen muss der Autor der Webkomponente die benutzerdefinierte Eigenschaft in seinem CSS verwenden, damit sie angewendet wird. Dies bedeutet, dass ein Autor einer Webkomponente eine benutzerdefinierte Eigenschafts-API erstellen kann, die Benutzer der Webkomponente verwenden können, um ihre eigenen Stile anzuwenden.
<template> <style> /* Declare the custom property API */ :host { --main-bg-color: brown; } .one { color: var(--main-bg-color); } </style> <div class="one">Hello World</div> </template> <script> /* Code to set up my-element web component */ </script> <my-element></my-element> <style> /* Override the custom variable with own value */ my-element { --main-bg-color: red; } </style>
Die Browserunterstützung für benutzerdefinierte Eigenschaften ist überraschend gut. Der einzige Grund, warum es keine Lösung ist, die Sie heute verwenden können, ist, dass es kein funktionierendes Polyfill ohne Custom Elements Version 1 gibt. Das Team hinter dem Polyfill von webcomponentjs arbeitet derzeit daran, es hinzuzufügen, aber es ist noch nicht veröffentlicht und in einem gebauten Zustand. Das heißt, wenn Sie Ihre Assets für die Produktion hashen, können Sie sie nicht verwenden. Soweit ich weiß, soll es Anfang nächsten Jahres erscheinen.
Trotzdem sind benutzerdefinierte Eigenschaften keine gute Methode zum Teilen von Stilen zwischen Webkomponenten. Da sie nur zum Deklarieren eines einzelnen Eigenschaftswerts verwendet werden können, müsste die Webkomponente weiterhin alle Stile des Styleguides einbetten, allerdings mit ihren Werten, die durch Variablen ersetzt werden.
Benutzerdefinierte Eigenschaften eignen sich eher für Themenoptionen als für gemeinsame Stile. Aus diesem Grund sind benutzerdefinierte Eigenschaften keine praktikable Lösung für unser Problem.
/* Benutzerdefinierte Eigenschaft verwenden */ input { background: var(–main-bg-color); } </style>
Eine Sache, die benutzerdefinierte Eigenschaften so mächtig macht, ist ihre Fähigkeit, das Schatten-DOM zu durchdringen. Dies ist nicht die gleiche Idee wie die Selektoren /deep/
und ::shadow
, da sie sich nicht in die Webkomponente drängen. Stattdessen muss der Autor der Webkomponente die benutzerdefinierte Eigenschaft in seinem CSS verwenden, damit sie angewendet wird. Dies bedeutet, dass ein Autor einer Webkomponente eine benutzerdefinierte Eigenschafts-API erstellen kann, die Benutzer der Webkomponente verwenden können, um ihre eigenen Stile anzuwenden.
<template> <style> /* Declare the custom property API */ :host { --main-bg-color: brown; } .one { color: var(--main-bg-color); } </style> <div class="one">Hello World</div> </template> <script> /* Code to set up my-element web component */ </script> <my-element></my-element> <style> /* Override the custom variable with own value */ my-element { --main-bg-color: red; } </style>
Die Browserunterstützung für benutzerdefinierte Eigenschaften ist überraschend gut. Der einzige Grund, warum es keine Lösung ist, die Sie heute verwenden können, ist, dass es kein funktionierendes Polyfill ohne Custom Elements Version 1 gibt. Das Team hinter dem Polyfill von webcomponentjs arbeitet derzeit daran, es hinzuzufügen, aber es ist noch nicht veröffentlicht und in einem gebauten Zustand. Das heißt, wenn Sie Ihre Assets für die Produktion hashen, können Sie sie nicht verwenden. Soweit ich weiß, soll es Anfang nächsten Jahres erscheinen.
Trotzdem sind benutzerdefinierte Eigenschaften keine gute Methode zum Teilen von Stilen zwischen Webkomponenten. Da sie nur zum Deklarieren eines einzelnen Eigenschaftswerts verwendet werden können, müsste die Webkomponente weiterhin alle Stile des Styleguides einbetten, allerdings mit ihren Werten, die durch Variablen ersetzt werden.
Benutzerdefinierte Eigenschaften eignen sich eher für Themenoptionen als für gemeinsame Stile. Aus diesem Grund sind benutzerdefinierte Eigenschaften keine praktikable Lösung für unser Problem.
@Apply-Regeln
Zusätzlich zu benutzerdefinierten Eigenschaften erhält CSS auch @apply
Regeln. Apply-Regeln sind im Wesentlichen Mixins für die CSS-Welt. Sie werden auf ähnliche Weise wie benutzerdefinierte Eigenschaften deklariert, können jedoch anstelle von Eigenschaftswerten zum Deklarieren von Gruppen von Eigenschaften verwendet werden. Genau wie benutzerdefinierte Eigenschaften können ihre Werte geerbt und überschrieben werden, und sie müssen in einem Selektor deklariert werden, um zu funktionieren.
<style> /* Declare rule */ html { --typography: { font: 16px Arial, sans-serif; color: #333333; } } /* Use rule */ input { @apply --typography; } </style>
Browserunterstützung für @apply
-Regeln ist im Grunde nicht vorhanden. Chrome unterstützt es derzeit hinter einem Feature-Flag (das ich nicht finden konnte), aber das war es auch schon. Aus dem gleichen Grund gibt es auch kein funktionierendes Polyfill, da es kein Polyfill für benutzerdefinierte Eigenschaften gibt. Das polyfill-Team von @apply
arbeitet außerdem daran, @apply-Regeln zusammen mit benutzerdefinierten Eigenschaften hinzuzufügen, sodass beide verfügbar sein werden, sobald die neue Version veröffentlicht wird.
Im Gegensatz zu benutzerdefinierten Eigenschaften sind @apply
Regeln eine viel bessere Lösung zum Teilen von Stilen. Da sie eine Gruppe von Eigenschaftsdeklarationen einrichten können, können Sie sie verwenden, um den Standardstil für alle nativen Elemente einzurichten und sie dann innerhalb der Webkomponente zu verwenden. Dazu müssten Sie für jedes native Element eine @apply
-Regel erstellen.
Um die Stile zu verwenden, müssten Sie sie jedoch manuell auf jedes native Element anwenden, wodurch die Stildeklaration immer noch in jeder Webkomponente dupliziert würde. Das ist zwar besser als das Einbetten aller Stile, aber auch nicht sehr praktisch, da es zu einer Boilerplate oben in jeder Webkomponente wird, die Sie hinzufügen müssen, damit die Stile ordnungsgemäß funktionieren.
/* styleguide.css */ html { --typography: { color: #333333; font: 16px Arial, sans-serif; } --paragraph: { margin: 0; } --label { display: block; margin-bottom: 5px; } --input-text { background-color: #eaeaea; border: 1px solid grey; border-radius: 4px; box-sizing: border-box; color: inherit; font: inherit; padding: 10px 10px; width: 100%; } --input-submit { font: 16px/1.6 Arial, sans-serif; color: white; background: cornflowerblue; border: 1px solid #1f66e5; border-radius: 4px; padding: 10px 10px; width: 100%; } /* And so on for every native element */ }
<!-- login-form.html --> <template> <style> :host { @apply --typography; } p { @apply --paragraph; } label { @apply --label; } input-text { @apply --input-text; } .input-submit { @apply --input-submit; } .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template>
Aufgrund der Notwendigkeit umfangreicher Textbausteine glaube ich nicht, dass @apply
Regeln eine gute Lösung für die gemeinsame Nutzung von Stilen zwischen Webkomponenten wären. Sie sind jedoch eine großartige Lösung für die Thematisierung.
im Schatten-DOM
Gemäß der Webkomponenten-Spezifikation ignorieren Browser alle <link rel=“stylesheet”>
Tags im Schatten-DOM und behandeln sie genauso, als würden sie es innerhalb eines Dokumentfragments tun. Dies hinderte uns daran, gemeinsame Stile in unseren Webkomponenten zu verlinken, was bedauerlich war – bis vor einigen Monaten, als die Webkomponenten-Arbeitsgruppe vorschlug, dass <link rel=“stylesheet”>
Tags funktionieren sollten das Schatten-DOM. Nach nur einer Woche Diskussion waren sich alle einig, dass sie das tun sollten, und ein paar Tage später fügten sie es der HTML-Spezifikation hinzu.
<template> <link rel="stylesheet" href="styleguide.css"> <style> .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- rest of template DOM --> </template>
Wenn das für die Arbeitsgruppe etwas zu schnell klingt, um sich auf eine Spezifikation zu einigen, liegt das daran, dass es sich nicht um einen neuen Vorschlag handelte. Dass link
-Tags im Schatten-DOM funktionieren, wurde eigentlich schon vor mindestens drei Jahren vorgeschlagen, aber es wurde zurückgedrängt, bis sichergestellt werden konnte, dass es kein Problem für die Leistung darstellt.
Wenn die Annahme des Vorschlags nicht aufregend genug ist, hat Chrome 55 (derzeit Chrome Canary) die anfängliche Funktionalität hinzugefügt, link
-Tags im Schatten-DOM funktionieren zu lassen. Es scheint sogar, dass diese Funktionalität in der aktuellen Version von Chrome gelandet ist. Sogar Safari hat das Feature in Safari 18 implementiert.
Die Möglichkeit, die gemeinsam genutzten Stile zu verlinken, ist bei weitem die bequemste Methode, um Stile zwischen Webkomponenten zu teilen. Alles, was Sie tun müssten, ist das link
-Tag zu erstellen, und alle nativen Elemente würden entsprechend gestylt, ohne dass zusätzliche Arbeit erforderlich wäre.
Natürlich wird die Art und Weise, wie Browserhersteller die Funktion implementieren, bestimmen, ob diese Lösung realisierbar ist. Damit dies ordnungsgemäß funktioniert, müssten link
-Tags dedupliziert werden, sodass mehrere Webkomponenten, die dieselbe CSS-Datei anfordern, nur eine HTTP-Anforderung verursachen würden. Das CSS müsste auch nur einmal analysiert werden, sodass jede Instanz der Webkomponente die gemeinsam genutzten Stile nicht neu berechnen müsste, sondern stattdessen die berechneten Stile wiederverwenden würde.
Chrome macht beides bereits. Wenn also alle anderen Browserhersteller es auf die gleiche Weise implementieren, dann würden link
-Tags, die im Schatten-DOM arbeiten, definitiv das Problem lösen, wie Stile zwischen Webkomponenten ausgetauscht werden können.
Konstruierbare Stylesheets
Sie werden es vielleicht kaum glauben können, da wir es noch nicht einmal haben, aber ein link
-Tag, das im Schatten-DOM funktioniert, ist keine langfristige Lösung. Stattdessen ist es nur eine kurzfristige Lösung, um uns zur wirklichen Lösung zu bringen: konstruierbare Stylesheets.
Konstruierbare Stylesheets sind ein Vorschlag, um die Erstellung von StyleSheet
Objekten in JavaScript über eine Konstruktorfunktion zu ermöglichen. Das erstellte Stylesheet könnte dann über eine API zum Shadow-DOM hinzugefügt werden, was es dem Shadow-DOM ermöglichen würde, einen Satz gemeinsam genutzter Stile zu verwenden.
Leider ist dies alles, was ich aus dem Vorschlag entnehmen konnte. Ich habe versucht, mehr Informationen darüber herauszufinden, was konstruierbare Stylesheets sind, indem ich die Web Components Working Group gefragt habe, aber sie haben mich auf die Mailingliste der CSS Working Group des W3C umgeleitet, wo ich erneut gefragt habe, aber niemand geantwortet hat. Ich konnte nicht einmal herausfinden, wie der Vorschlag vorankam, weil er seit über zwei Jahren nicht aktualisiert wurde.
Trotzdem verwendet die Web Components Working Group es als Lösung für die gemeinsame Nutzung von Stilen zwischen Webkomponenten. Hoffentlich wird entweder der Vorschlag aktualisiert oder die Web Components Working Group wird weitere Informationen darüber und seine Annahme veröffentlichen. Bis dahin scheint die „langfristige“ Lösung in absehbarer Zeit nicht zu kommen.
gewonnene Erkenntnisse
Nach monatelangem Forschen und Testen blicke ich recht hoffnungsvoll in die Zukunft. Es ist beruhigend zu wissen, dass es nach Jahren ohne Lösung für die gemeinsame Nutzung von Stilen zwischen Webkomponenten endlich Antworten gibt. Diese Antworten werden vielleicht erst in ein paar Jahren etabliert, aber zumindest sind sie da.
Wenn Sie heute einen gemeinsam genutzten Styleguide verwenden möchten, um Webkomponenten zu stylen, können Sie entweder das Schatten-DOM nicht verwenden und stattdessen benutzerdefinierte Elemente erstellen, oder Sie können eine Webkomponentenbibliothek verwenden, die Polyfill-Unterstützung für die gemeinsame Nutzung von Stilen bietet. Beide Lösungen haben ihre Vor- und Nachteile, also verwenden Sie diejenige, die für Ihr Projekt am besten geeignet ist.
Wenn Sie sich entscheiden, eine Weile zu warten, bevor Sie sich mit Webkomponenten befassen, sollten wir in ein paar Jahren einige großartige Lösungen haben, um die Stile zwischen ihnen zu teilen. Schauen Sie also immer wieder nach, wie es vorangeht.
Dinge, die Sie beachten sollten
Beachten Sie einige Dinge, wenn Sie sich heute für die Verwendung benutzerdefinierter Elemente oder Webkomponenten entscheiden.
Am wichtigsten ist, dass die Webkomponentenspezifikation immer noch aktiv weiterentwickelt wird, was bedeutet, dass sich die Dinge ändern können und werden. Web-Komponenten sind immer noch sehr auf dem neuesten Stand, seien Sie also darauf vorbereitet, auf Trab zu bleiben, während Sie damit entwickeln.
Wenn Sie sich für die Verwendung des Schatten-DOM entscheiden, sollten Sie wissen, dass es in polygefüllten Browsern ziemlich langsam und leistungsschwach ist. Aus diesem Grund haben die Polymer-Entwickler ihre zwielichtige DOM-Implementierung erstellt und zu ihrem Standard gemacht.
Chrome, Opera und neuerdings Safari sind die einzigen Browser, die das Shadow DOM Version 0 unterstützen. Firefox befindet sich noch in der Entwicklung, obwohl es seit Version 29 hinter einem Experiment unterstützt wird. Microsoft erwägt es immer noch für Edge und hat es als hohe Priorität auf seiner Roadmap.
Shadow DOM Version 0 ist jedoch die alte Spezifikation. Shadow DOM Version 1 ist die neue und wird nur von Chrome, Safari und Opera vollständig unterstützt. Ganz zu schweigen davon, dass benutzerdefinierte Elemente, Version 0, dasselbe Upgrade durchlaufen haben und nur Chrome benutzerdefinierte Elemente, Version 1, vollständig unterstützt, während die technische Vorschau von Safari dies ab Version 17 unterstützt. Benutzerdefinierte Elemente, Version 1, weisen einige wesentliche Änderungen in der Art und Weise auf, wie Webkomponenten geschrieben werden. Stellen Sie also sicher, dass Sie vollständig verstehen, was das bedeutet.
Schließlich unterstützt das webcomponentjs-Polyfill nur die Version 0-Implementierung des Schatten-DOM und benutzerdefinierter Elemente. Ein Version 1-Zweig des Polyfill unterstützt Version 1, ist aber noch nicht veröffentlicht.