Styler des composants Web à l'aide d'une feuille de style partagée

Publié: 2022-03-10
Résumé rapide ↬ Les composants Web sont une nouvelle fonctionnalité étonnante du Web, permettant aux développeurs de définir leurs propres éléments HTML personnalisés. Lorsqu'ils sont combinés avec un guide de style, les composants Web peuvent créer une API de composant, ce qui permet aux développeurs d'arrêter de copier et coller des extraits de code et d'utiliser à la place un élément DOM. En utilisant le DOM fantôme, nous pouvons encapsuler le composant Web et ne pas avoir à nous soucier des guerres de spécificité avec toute autre feuille de style sur la page. Cependant, les composants Web et les guides de style semblent actuellement en contradiction les uns avec les autres.

Les composants Web sont une nouvelle fonctionnalité étonnante du Web, permettant aux développeurs de définir leurs propres éléments HTML personnalisés. Lorsqu'ils sont combinés avec un guide de style, les composants Web peuvent créer une API de composant, ce qui permet aux développeurs d'arrêter de copier et coller des extraits de code et d'utiliser à la place un élément DOM. En utilisant le DOM fantôme, nous pouvons encapsuler le composant Web et ne pas avoir à nous soucier des guerres de spécificité avec toute autre feuille de style sur la page.

Cependant, les composants Web et les guides de style semblent actuellement en contradiction les uns avec les autres. D'une part, les guides de style fournissent un ensemble de règles et de styles qui s'appliquent globalement à la page et assurent la cohérence sur l'ensemble du site Web. D'autre part, les composants Web avec le shadow DOM empêchent tout style global de pénétrer dans leur encapsulation, empêchant ainsi le guide de style de les affecter.

Lectures complémentaires sur SmashingMag :

  • Application des meilleures pratiques dans les systèmes à base de composants
  • Comment utiliser le préprocesseur CSS LESS pour des feuilles de style plus intelligentes
  • Une plongée profonde dans Adobe Edge Reflow

Alors, comment les deux peuvent-ils coexister, avec des guides de style globaux continuant à fournir de la cohérence et des styles, même aux composants Web avec le DOM fantôme ? Heureusement, il existe des solutions qui fonctionnent aujourd'hui, et d'autres solutions à venir, qui permettent aux guides de style globaux de fournir un style aux composants Web. (Pour le reste de cet article, j'utiliserai le terme "composants Web" pour faire référence aux éléments personnalisés avec le shadow DOM.)

Plus après saut! Continuez à lire ci-dessous ↓

Que doit faire un style de guide de style global dans un composant Web ?

Avant de discuter de la façon d'obtenir un guide de style global pour styliser un composant Web, nous devrions discuter de ce qu'il devrait et ne devrait pas essayer de styliser.

Tout d'abord, les meilleures pratiques actuelles pour les composants Web stipulent qu'un composant Web, y compris ses styles, doit être encapsulé, afin qu'il ne dépende d'aucune ressource externe pour fonctionner. Cela lui permet d'être utilisé n'importe où sur ou hors du site Web, même lorsque le guide de style n'est pas disponible.

Vous trouverez ci-dessous un composant Web de formulaire de connexion simple qui encapsule tous ses styles.

 <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>

Remarque : Des exemples de code sont écrits dans la spécification de la version 1 pour les composants Web.

Formulaire de connexion avec nom d'utilisateur et mot de passe.
Un composant Web de formulaire de connexion simple

Cependant, l'encapsulation complète de chaque composant Web entraînerait inévitablement de nombreux CSS en double, en particulier lorsqu'il s'agit de configurer la typographie et le style des éléments natifs. Si un développeur souhaite utiliser un paragraphe, une balise d'ancrage ou un champ de saisie dans son composant Web, il doit être stylisé comme le reste du site Web.

Si nous encapsulons entièrement tous les styles dont le composant Web a besoin, le CSS pour les paragraphes de style, les balises d'ancrage, les champs de saisie, etc. serait dupliqué sur tous les composants Web qui les utilisent. Cela augmenterait non seulement les coûts de maintenance, mais conduirait également à des tailles de téléchargement beaucoup plus importantes pour les utilisateurs.

Au lieu d'encapsuler tous les styles, les composants Web ne doivent encapsuler que leurs styles uniques, puis dépendre d'un ensemble de styles partagés pour gérer les styles pour tout le reste. Ces styles partagés deviendraient essentiellement une sorte de Normalize.css, que les composants Web pourraient utiliser pour s'assurer que les éléments natifs sont stylisés conformément au guide de style.

Dans l'exemple précédent, le composant Web du formulaire de connexion ne déclarerait les styles que pour ses deux classes uniques : .container et .footnote . Le reste des styles appartiendrait à la feuille de style partagée et styliserait les paragraphes, les balises d'ancrage, les champs de saisie, etc.

En bref, le guide de style ne doit pas essayer de styliser le composant Web, mais plutôt fournir un ensemble de styles partagés que les composants Web peuvent utiliser pour obtenir une apparence cohérente.

Comment styliser le DOM Shadow avec des feuilles de style externes était fait

La spécification initiale des composants Web (connue sous le nom de version 0) permettait à toute feuille de style externe de pénétrer le DOM fantôme grâce à l'utilisation des sélecteurs ::shadow ou /deep/ CSS. L'utilisation de ::shadow et /deep/ vous a permis de faire pénétrer un guide de style dans le shadow DOM et de configurer les styles partagés, que le composant Web le veuille ou non.

 /* Style all p tags inside a web components shadow DOM */ login-form::shadow p { color: red; }

Avec l'avènement de la dernière version de la spécification des composants Web (connue sous le nom de version 1), les auteurs ont supprimé la capacité des feuilles de style externes à pénétrer le DOM fantôme, et ils n'ont fourni aucune alternative. Au lieu de cela, la philosophie a changé, passant de l'utilisation de dragons pour styliser les composants Web à l'utilisation de ponts. En d'autres termes, les auteurs de composants Web devraient être responsables des règles de style externes autorisées à styliser leur composant, plutôt que d'être obligés de les autoriser.

Malheureusement, cette philosophie n'a pas encore vraiment rattrapé le Web, ce qui nous laisse un peu dans le pétrin. Heureusement, quelques solutions disponibles aujourd'hui, et certaines à venir dans un avenir pas si lointain, permettront à une feuille de style partagée de styliser un composant Web.

Ce que vous pouvez faire aujourd'hui

Il existe trois techniques que vous pouvez utiliser aujourd'hui pour permettre à un composant Web de partager des styles : @import , des éléments personnalisés et une bibliothèque de composants Web.

Utiliser @import

Le seul moyen natif aujourd'hui d'intégrer une feuille de style dans un composant Web consiste à utiliser @import . Bien que cela fonctionne, c'est un anti-modèle. Pour les composants Web, cependant, il s'agit d'un problème de performances encore plus important.

 <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>

Normalement, @import est un anti-modèle car il télécharge toutes les feuilles de style en série, plutôt qu'en parallèle, surtout si elles sont imbriquées. Dans notre situation, le téléchargement d'une seule feuille de style en série ne peut pas être aidé, donc en théorie, ça devrait aller. Mais lorsque j'ai testé cela dans Chrome, les résultats ont montré que l'utilisation @import le rendu de la page jusqu'à une demi-seconde par rapport à l'intégration directe des styles dans le composant Web.

Remarque : En raison des différences de fonctionnement du polyfill des importations HTML par rapport aux importations HTML natives, WebPagetest.org ne peut être utilisé pour donner des résultats fiables que dans les navigateurs qui prennent en charge nativement les importations HTML (c'est-à-dire Chrome).

Graphique à barres des performances des composants Web natifs.
Les résultats de trois tests de performances montrent que @import le rendu du navigateur jusqu'à une demi-seconde par rapport à l'intégration directe des styles dans le composant Web.

Au final, @import reste un anti-pattern et peut être un problème de performance dans les composants web. Ce n'est donc pas une excellente solution.

N'utilisez pas le Shadow DOM

Étant donné que le problème d'essayer de fournir des styles partagés aux composants Web provient de l'utilisation du DOM fantôme, une façon d'éviter complètement le problème consiste à ne pas utiliser le DOM fantôme.

En n'utilisant pas le DOM fantôme, vous créerez des éléments personnalisés au lieu de composants Web (voir ci-dessous), la seule différence étant l'absence de DOM fantôme et de portée. Votre élément sera soumis aux styles de la page, mais nous devons déjà nous en occuper aujourd'hui, donc ce n'est rien que nous ne sachions déjà gérer. Les éléments personnalisés sont entièrement pris en charge par le polyfill webcomponentjs, qui offre une excellente prise en charge des navigateurs.

Le plus grand avantage des éléments personnalisés est que vous pouvez créer une bibliothèque de modèles en les utilisant dès aujourd'hui, et vous n'avez pas à attendre que le problème du style partagé soit résolu. Et comme la seule différence entre les composants Web et les éléments personnalisés est le shadow DOM, vous pouvez toujours activer le shadow DOM dans vos éléments personnalisés une fois qu'une solution de style partagé est disponible.

Si vous décidez de créer des éléments personnalisés, tenez compte des quelques différences entre les éléments personnalisés et les composants Web.

Tout d'abord, étant donné que les styles de l'élément personnalisé sont soumis aux styles de page et vice versa, vous devez vous assurer que vos sélecteurs ne provoquent aucun conflit. Si vos pages utilisent déjà un guide de style, laissez les styles de l'élément personnalisé dans le guide de style et faites en sorte que l'élément produise le DOM et la structure de classe attendus.

En laissant les styles dans le guide de style, vous créerez un chemin de migration fluide pour vos développeurs, car ils pourront continuer à utiliser le guide de style comme avant, mais migreront ensuite lentement vers l'utilisation du nouvel élément personnalisé lorsqu'ils le pourront. Une fois que tout le monde utilise l'élément personnalisé, vous pouvez déplacer les styles pour qu'ils résident à l'intérieur de l'élément afin de les conserver ensemble et de permettre une refactorisation plus facile des composants Web ultérieurement.

Deuxièmement, assurez-vous d'encapsuler tout code JavaScript dans une expression de fonction appelée immédiatement (IFFE), afin de ne pas saigner de variables dans la portée globale. En plus de ne pas fournir de portée CSS, les éléments personnalisés ne fournissent pas de portée JavaScript.

Troisièmement, vous devrez utiliser la fonction connectedCallback de l'élément personnalisé pour ajouter le modèle DOM à l'élément. Selon la spécification du composant Web, les éléments personnalisés ne doivent pas ajouter d'enfants pendant la fonction constructeur, vous devrez donc différer l'ajout du DOM à la fonction connectedCallback .

Enfin, l'élément <slot> ne fonctionne pas en dehors du shadow DOM. Cela signifie que vous devrez utiliser une méthode différente pour permettre aux développeurs d'insérer leur contenu dans votre élément personnalisé. Habituellement, cela implique simplement de manipuler le DOM vous-même pour insérer leur contenu là où vous le souhaitez.

Cependant, comme il n'y a pas de séparation entre le DOM shadow et le DOM light avec des éléments personnalisés, vous devrez également faire très attention à ne pas styliser le DOM inséré, en raison des styles en cascade de vos éléments.

 <!-- 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>

En termes de performances, les éléments personnalisés sont presque aussi rapides que les composants Web non utilisés (c'est-à-dire en liant la feuille de style partagée dans la head et en utilisant uniquement des éléments DOM natifs). De toutes les techniques que vous pouvez utiliser aujourd'hui, c'est de loin la plus rapide.

Graphique à barres des performances des éléments personnalisés.
Les résultats de deux tests de performances montrent que les éléments personnalisés sont presque aussi rapides que l'absence totale de composants Web.

À part : un élément personnalisé est toujours un composant Web à toutes fins utiles. Le terme "composants Web" est utilisé pour décrire quatre technologies distinctes : les éléments personnalisés, les balises de modèle, les importations HTML et le DOM fantôme.

Malheureusement, le terme a été utilisé pour décrire tout ce qui utilise une combinaison des quatre technologies. Cela a conduit à beaucoup de confusion quant à ce que les gens veulent dire lorsqu'ils parlent de "composant Web". Tout comme Rob Dodson l'a découvert, j'ai trouvé utile d'utiliser des termes différents pour parler d'éléments personnalisés avec et sans le DOM fantôme.

La plupart des développeurs à qui j'ai parlé ont tendance à associer le terme "composant Web" à un élément personnalisé qui utilise le DOM fantôme. Ainsi, pour les besoins de cet article, j'ai créé une distinction artificielle entre un composant Web et un élément personnalisé.

Utilisation d'une bibliothèque de composants Web

Une autre solution que vous pouvez utiliser aujourd'hui est une bibliothèque de composants Web, telle que Polymer, SkateJS ou X-Tag. Ces bibliothèques aident à combler les lacunes du support actuel et peuvent également simplifier le code nécessaire à la création d'un composant Web. Ils fournissent également généralement des fonctionnalités supplémentaires qui facilitent l'écriture de composants Web.

Par exemple, Polymer vous permet de créer un composant Web simple en quelques lignes de JavaScript. Un avantage supplémentaire est que Polymer fournit une solution pour utiliser le DOM fantôme et une feuille de style partagée. Cela signifie que vous pouvez créer dès aujourd'hui des composants Web qui partagent des styles.

Pour ce faire, créez ce qu'ils appellent un module de style, qui contient tous les styles partagés. Il peut s'agir soit d'une <style> avec les styles partagés en ligne, soit d'une <link rel=“import”> qui pointe vers une feuille de style partagée. Dans les deux cas, incluez les styles dans votre composant Web avec une <style include> , puis Polymer analysera les styles et les ajoutera en tant que <style> en ligne à votre composant Web.

 <!-- 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>

Le seul inconvénient de l'utilisation d'une bibliothèque est qu'elle peut retarder le temps de rendu de vos composants Web. Cela ne devrait pas être une surprise car le téléchargement du code de la bibliothèque et son traitement prennent du temps. Les composants Web de la page ne peuvent pas commencer le rendu tant que la bibliothèque n'a pas terminé le traitement.

Dans le cas de Polymer, il peut retarder le temps de rendu de la page jusqu'à une demi-seconde par rapport aux composants Web natifs. Un module de style qui intègre les styles est légèrement plus lent qu'un module de style qui lie les styles, et l'intégration des styles directement dans le composant Web est tout aussi rapide que l'utilisation d'un module de style.

Encore une fois, Polymer ne fait rien de particulier pour ralentir le temps de rendu. Le téléchargement de la bibliothèque Polymer et le traitement de toutes ses fonctionnalités impressionnantes, ainsi que la création de toutes les liaisons de modèles, prennent du temps. C'est juste le compromis que vous devrez faire pour utiliser une bibliothèque de composants Web.

Graphique à barres des performances des composants Web en polymère.

Les résultats des tests de performances montrent qu'en utilisant Polymer, les composants Web seront rendus jusqu'à une demi-seconde plus lentement que les composants Web natifs.

La promesse du futur

Si aucune des solutions actuelles ne fonctionne pour vous, ne désespérez pas. Si tout va bien, d'ici quelques mois à quelques années, nous serons en mesure d'utiliser des styles partagés en utilisant quelques approches différentes.

Propriétés personnalisées

Les propriétés personnalisées (ou variables CSS, comme on les appelle) sont un moyen de définir et d'utiliser des variables dans CSS. Cette notion n'est pas nouvelle pour les préprocesseurs CSS, mais en tant que fonctionnalité CSS native, les propriétés personnalisées sont en fait plus puissantes qu'une variable de préprocesseur.

Pour déclarer une propriété personnalisée, utilisez la notation de propriété personnalisée de –my-variable: value et accédez à la variable à l'aide de property: var(–my-variable) . Une propriété personnalisée cascade comme n'importe quelle autre règle CSS, de sorte que sa valeur hérite de son parent et peut être remplacée. La seule mise en garde concernant les propriétés personnalisées est qu'elles doivent être déclarées dans un sélecteur et ne peuvent pas être déclarées seules, contrairement à une variable de préprocesseur.

 <style> /* Declare the custom property */ html { --main-bg-color: red; } /* Use the custom property */ input { background: var(--main-bg-color); } </style>

Une chose qui rend les propriétés personnalisées si puissantes est leur capacité à percer le DOM fantôme. Ce n'est pas la même idée que les sélecteurs /deep/ et ::shadow car ils ne se frayent pas un chemin dans le composant Web. Au lieu de cela, l'auteur du composant Web doit utiliser la propriété personnalisée dans son CSS pour qu'elle soit appliquée. Cela signifie qu'un auteur de composant Web peut créer une API de propriété personnalisée que les consommateurs du composant Web peuvent utiliser pour appliquer leurs propres styles.

 <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>

La prise en charge des navigateurs pour les propriétés personnalisées est étonnamment bonne. La seule raison pour laquelle ce n'est pas une solution que vous pouvez utiliser aujourd'hui est qu'il n'y a pas de polyfill fonctionnel sans Custom Elements version 1. L'équipe derrière le polyfill webcomponentjs travaille actuellement pour l'ajouter, mais il n'est pas encore publié et dans un état construit, ce qui signifie que si vous hachez vos actifs pour la production, vous ne pouvez pas les utiliser. D'après ce que j'ai compris, il devrait sortir au début de l'année prochaine.

Même ainsi, les propriétés personnalisées ne sont pas une bonne méthode pour partager des styles entre des composants Web. Comme ils ne peuvent être utilisés que pour déclarer une seule valeur de propriété, le composant Web devrait toujours intégrer tous les styles du guide de style, même si leurs valeurs sont remplacées par des variables.

Les propriétés personnalisées sont plus adaptées aux options de thème qu'aux styles partagés. Pour cette raison, les propriétés personnalisées ne sont pas une solution viable à notre problème.

/* Utilisez la propriété personnalisée */ input { background: var(–main-bg-color); } </style>

Une chose qui rend les propriétés personnalisées si puissantes est leur capacité à percer le DOM fantôme. Ce n'est pas la même idée que les sélecteurs /deep/ et ::shadow car ils ne se frayent pas un chemin dans le composant Web. Au lieu de cela, l'auteur du composant Web doit utiliser la propriété personnalisée dans son CSS pour qu'elle soit appliquée. Cela signifie qu'un auteur de composant Web peut créer une API de propriété personnalisée que les consommateurs du composant Web peuvent utiliser pour appliquer leurs propres styles.

 <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>

La prise en charge des navigateurs pour les propriétés personnalisées est étonnamment bonne. La seule raison pour laquelle ce n'est pas une solution que vous pouvez utiliser aujourd'hui est qu'il n'y a pas de polyfill fonctionnel sans Custom Elements version 1. L'équipe derrière le polyfill webcomponentjs travaille actuellement pour l'ajouter, mais il n'est pas encore publié et dans un état construit, ce qui signifie que si vous hachez vos actifs pour la production, vous ne pouvez pas les utiliser. D'après ce que j'ai compris, il devrait sortir au début de l'année prochaine.

Même ainsi, les propriétés personnalisées ne sont pas une bonne méthode pour partager des styles entre des composants Web. Comme ils ne peuvent être utilisés que pour déclarer une seule valeur de propriété, le composant Web devrait toujours intégrer tous les styles du guide de style, même si leurs valeurs sont remplacées par des variables.

Les propriétés personnalisées sont plus adaptées aux options de thème qu'aux styles partagés. Pour cette raison, les propriétés personnalisées ne sont pas une solution viable à notre problème.

@appliquer les règles

En plus des propriétés personnalisées, CSS obtient également des règles @apply . Les règles d'application sont essentiellement des mixins pour le monde CSS. Elles sont déclarées de la même manière que les propriétés personnalisées, mais peuvent être utilisées pour déclarer des groupes de propriétés au lieu de simplement des valeurs de propriété. Tout comme les propriétés personnalisées, leurs valeurs peuvent être héritées et remplacées, et elles doivent être déclarées dans un sélecteur pour fonctionner.

 <style> /* Declare rule */ html { --typography: { font: 16px Arial, sans-serif; color: #333333; } } /* Use rule */ input { @apply --typography; } </style>

La prise en charge des navigateurs pour les règles @apply est pratiquement inexistante. Chrome le prend actuellement en charge derrière un indicateur de fonctionnalité (que je n'ai pas trouvé), mais c'est à peu près tout. Il n'y a pas non plus de polyfill fonctionnel pour la même raison qu'il n'y a pas de polyfill pour les propriétés personnalisées. L'équipe polyfill de webcomponentjs travaille également pour ajouter des règles @apply , ainsi que des propriétés personnalisées, afin que les deux soient disponibles une fois la nouvelle version publiée.

Contrairement aux propriétés personnalisées, les règles @apply sont une bien meilleure solution pour partager des styles. Comme ils peuvent configurer un groupe de déclarations de propriété, vous pouvez les utiliser pour configurer le style par défaut de tous les éléments natifs, puis les utiliser dans le composant Web. Pour ce faire, vous devez créer une règle @apply pour chaque élément natif.

Cependant, pour utiliser les styles, vous devrez les appliquer manuellement à chaque élément natif, ce qui dupliquerait toujours la déclaration de style dans chaque composant Web. Bien que ce soit mieux que d'intégrer tous les styles, ce n'est pas très pratique non plus, car cela devient un passe-partout en haut de chaque composant Web, que vous devez vous rappeler d'ajouter pour que les styles fonctionnent correctement.

 /* 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>

En raison de la nécessité d'un passe-partout étendu, je ne pense pas que les règles @apply soient une bonne solution pour partager des styles entre des composants Web. Ils sont une excellente solution pour la thématisation, cependant.

dans le Shadow DOM

Selon la spécification du composant Web, les navigateurs ignorent toutes les balises <link rel=“stylesheet”> dans le shadow DOM, les traitant comme ils le feraient à l'intérieur d'un fragment de document. Cela nous empêchait de pouvoir lier des styles partagés dans nos composants Web, ce qui était regrettable - c'est-à-dire jusqu'à il y a quelques mois, lorsque le groupe de travail sur les composants Web a proposé que les balises <link rel=“stylesheet”> fonctionnent dans le DOM fantôme. Après seulement une semaine de discussion, ils ont tous convenu qu'ils devraient, et quelques jours plus tard, ils l'ont ajouté à la spécification HTML.

 <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>

Si cela semble un peu trop rapide pour que le groupe de travail s'entende sur une spécification, c'est parce qu'il ne s'agissait pas d'une nouvelle proposition. Faire fonctionner les balises de link dans le DOM fantôme a été proposé il y a au moins trois ans, mais cela a été retardé jusqu'à ce qu'ils puissent s'assurer que ce n'était pas un problème de performances.

Si l'acceptation de la proposition n'est pas assez excitante, Chrome 55 (actuellement Chrome Canary) a ajouté la fonctionnalité initiale de faire fonctionner les balises de link dans le DOM fantôme. Il semblerait même que cette fonctionnalité ait débarqué dans la version actuelle de Chrome. Même Safari a implémenté la fonctionnalité dans Safari 18.

Pouvoir lier les styles partagés est de loin la méthode la plus pratique pour partager des styles entre des composants Web. Tout ce que vous auriez à faire est de créer la balise de link , et tous les éléments natifs seraient stylisés en conséquence, sans nécessiter de travail supplémentaire.

Bien sûr, la façon dont les fabricants de navigateurs implémentent la fonctionnalité déterminera si cette solution est viable. Pour que cela fonctionne correctement, les balises de link doivent être dédupliquées, de sorte que plusieurs composants Web demandant le même fichier CSS ne provoquent qu'une seule requête HTTP. Le CSS n'aurait également besoin d'être analysé qu'une seule fois, de sorte que chaque instance du composant Web n'aurait pas à recalculer les styles partagés, mais réutiliserait à la place les styles calculés.

Chrome fait déjà les deux. Ainsi, si tous les autres fabricants de navigateurs l'implémentent de la même manière, les balises de link fonctionnant dans le DOM fantôme résoudraient définitivement le problème du partage des styles entre les composants Web.

Feuilles de style constructibles

Vous aurez peut-être du mal à le croire, puisque nous ne l'avons même pas encore, mais une balise de link fonctionnant dans le DOM fantôme n'est pas une solution à long terme. Au lieu de cela, c'est juste une solution à court terme pour nous amener à la vraie solution : des feuilles de style constructibles.

Les feuilles de style constructibles sont une proposition pour permettre la création d'objets StyleSheet en JavaScript via une fonction constructeur. La feuille de style construite pourrait ensuite être ajoutée au DOM fantôme via une API, ce qui permettrait au DOM fantôme d'utiliser un ensemble de styles partagés.

Malheureusement, c'est tout ce que j'ai pu retenir de la proposition. J'ai essayé de trouver plus d'informations sur les feuilles de style constructibles en demandant au groupe de travail sur les composants Web, mais ils m'ont redirigé vers la liste de diffusion du groupe de travail CSS du W3C, où j'ai demandé à nouveau, mais personne n'a répondu. Je ne pouvais même pas comprendre comment la proposition progressait, car elle n'a pas été mise à jour depuis plus de deux ans.

Même ainsi, le groupe de travail sur les composants Web l' utilise comme solution pour partager les styles entre les composants Web. Espérons que soit la proposition sera mise à jour, soit le groupe de travail sur les composants Web publiera plus d'informations à son sujet et sur son adoption. Jusque-là, la solution "à long terme" semble ne pas se produire dans un avenir prévisible.

Leçons apprises

Après des mois de recherche et d'essais, j'ai bon espoir pour l'avenir. Il est réconfortant de savoir qu'après des années sans solution pour partager les styles entre les composants Web, il existe enfin des réponses. Ces réponses ne seront peut-être pas établies avant quelques années, mais au moins elles sont là.

Si vous souhaitez utiliser un guide de style partagé pour styliser les composants Web aujourd'hui, soit vous ne pouvez pas utiliser le DOM fantôme et créez à la place des éléments personnalisés, soit vous pouvez utiliser une bibliothèque de composants Web prise en charge par les polyfills pour le partage des styles. Les deux solutions ont leurs avantages et leurs inconvénients, alors utilisez celle qui convient le mieux à votre projet.

Si vous décidez d'attendre un peu avant de vous plonger dans les composants Web, dans quelques années, nous devrions avoir d'excellentes solutions pour partager les styles entre eux. Alors, continuez à vérifier comment cela progresse.

Choses à garder à l'esprit

Gardez à l'esprit certaines choses si vous décidez d'utiliser des éléments personnalisés ou des composants Web aujourd'hui.

Plus important encore, la spécification des composants Web est toujours en cours de développement, ce qui signifie que les choses peuvent et vont changer. Les composants Web sont toujours à la pointe de la technologie, alors soyez prêt à rester sur vos gardes pendant que vous développez avec.

Si vous décidez d'utiliser le shadow DOM, sachez qu'il est assez lent et peu performant dans les navigateurs polychargés. C'est pour cette raison que les développeurs de Polymer ont créé leur implémentation DOM douteuse et en ont fait leur défaut.

Chrome, Opera et, récemment, Safari sont les seuls navigateurs qui prennent en charge la version 0 du shadow DOM. Firefox est toujours en développement, bien qu'il l'ait pris en charge derrière une expérience depuis la version 29. Microsoft l'envisage toujours pour Edge et l'a comme un haute priorité sur sa feuille de route.

Cependant, la version 0 du shadow DOM est l'ancienne spécification. La version 1 de Shadow DOM est la nouvelle, et seuls Chrome, Safari et Opera la prennent pleinement en charge. Sans oublier que la version 0 des éléments personnalisés a subi la même mise à niveau et que seul Chrome prend entièrement en charge la version 1 des éléments personnalisés, alors que l'aperçu technique de Safari le prend en charge à partir de la version 17. La version 1 des éléments personnalisés comporte des changements majeurs dans la façon dont les composants Web sont écrits, alors assurez-vous de bien comprendre ce que cela implique.

Enfin, le polyfill webcomponentjs ne prend en charge que l'implémentation de la version 0 du shadow DOM et des éléments personnalisés. Une branche version 1 du polyfill prendra en charge la version 1, mais elle n'est pas encore publiée.