Styling your WebComponents 💄

Les composants web ne sont rien de plus que l’habituel combo HTML/CSS/JS. Pourtant, lorsque vous voudrez commencer à y ajouter du style, vous aller rapidement vous prendre un mur : la barriùre du scope Shadow DOM.

Je ne vais pas rĂ©-expliquer ce qu’est le Shadow DOM, ceci ayant dĂ©jĂ  Ă©tĂ© fait dans un prĂ©cĂ©dent article. Mais pour fixer le contexte, je vais juste vous rappeler que le shadow DOM permet d’isoler le HTML afin qu’il ne subisse pas les effets dĂ©finis dans la page qui l’intĂšgre. En gros, quel que soit les styles dĂ©finis dans la page qui intĂšgre le composant, jamais ils ne pourront modifier le style de ce dernier.

Jamais ? Vraiment ? En fait si, peut-ĂȘtre bien qu’il y’a un moyen et c’est ce que nous allons voir dans la suite de cet article.


Avec des styles encapsulés

OĂč les dĂ©clarer ?

Afin que notre composant isole ses propres styles, c’est dans le composants lui-mĂȘme qu’on va les dĂ©clarer. On les intĂ©grera donc dans le shadow root afin qu’ils soient encapsulĂ©s.

Styliser l’élĂ©ment Custom Element

L’élĂ©ment Custom Element (donc la balise html custom qui va contenir notre composant) peut lui aussi ĂȘtre stylisĂ©. Bien que la dĂ©claration du custom elements soit hors du shadow root, les styles encapsulĂ©s pourront l’atteindre grĂące au sĂ©lecteur :host, :host(.classname) .

:hostvous permettra de styliser l’élĂ©ment wrapper de votre composant depuis un style dĂ©clarĂ© dans le shadow DOM. Il sera possible de filtrer un host uniquement dans certains cas (ex: s’il a une classe particuliĂšre) en passant un sĂ©lecteur dans la paranthĂšse.

Example :
:host { color: red; }
 :host(.different) { color: green; }

:host-context pseudo selector

Dans certains cas, nous aimerions ajouter un style Ă  un Ă©lĂ©ment qui est imbriquĂ© dans plusieurs types de composants diffĂ©rents. Imaginez par exemple que vous avez un <input> qui est Ă  la fois dans un composant <magic-field> et dans <amazing-field>. Comme ces composants n’ont pas le mĂȘme but, ils ne seront pas affichĂ©s de la mĂȘme maniĂšre et donc il faudra dĂ©clarer des styles diffĂ©rents selon le context qu’est le composant.

Le pseudo selector :host-context est la solution pour cette problématique, car il nous permettra de cibler un contexte précis.

:host-context(magic-field) input { border: 1px solid red; }
:host-context(amazing-field) input { border: 1px solid pink; }

Styliser les noeuds distribués

Les noeuds distribuĂ©s sont des noeuds qui n’existe pas dans la dĂ©finition du composant, mais sont passĂ© par le parent en tant que contenu lors de la dĂ©claration (aussi appelĂ© transclusion).

Par exemple pour passer un titre au composant nous utiliserons :

<my-webcomponent>
<h1>Hello world</h1>
</my-webcomponent>

Ensuite pour styliser ce contenu, nous passerons par le selector ::content qui nous permettra de cibler un slot transfĂ©rĂ© par le parent. Pour ensuite naviguer dans le html transmis, il suffira d’ajouter des sĂ©lecteurs comme nous avons l’habitude de le faire en CSS.

Exemple de selecteur pour les titres de niveau 1 passés par le parent : ::content h1 { color: blue; }


Avec des styles de l’application parentes

Bien qu’il y’ait une “barriĂšre” qui empĂȘche les styles de l’application parente de modifier les styles encapsulĂ©s, il est possible de volontairement forcer cette limitation.

Atteindre l’intĂ©rieur d’un shadow-dom

Depuis le light DOM de l’application parente, l’utilisation du pseudo selector ::shadow nous permettra de traverser le cloisonnement du shadow-DOM. On pourra ainsi atteindre des Ă©lĂ©ments cachĂ©s et les styliser comme s’ils Ă©taient dĂ©clarĂ©s directement dans notre application. L’avantage de devoir spĂ©cifier ce pseudo-selector est que les styles ne s’appliqueront pas par dĂ©faut, mais qu’il faudra explicitement les dĂ©finir afin que les styles ne soient pas appliquĂ©s par erreur.

Etant un pseudo-sĂ©lector, ::shadow ne s’utilise pas tout seul, mais en combinaison avec d’autres sĂ©lecteurs.

Example :
my-webcomp::shadow .title { color: red; }
#host::shadow .text {color: blue; }

Accéder plusieurs niveaux de shadow Dom imbriqués

Dans certains cas, il se peut qu’il y’ait plusieurs niveaux d’imbrication de Shadow-DOM. Si vous ĂȘtes fan d’Inception et que vous rencontrez ce cas, vous aurez certainement besoin de connaĂźtre le sĂ©lecteur /deep/ .

Ce mot parle de lui-mĂȘme et permet de sĂ©lectionner des Ă©lĂ©ments au travers de plusieurs barriĂšre Shadow-DOM.

Eviter le FOUC

Le FOUC est un acronyme pour “Flash of unstyled content”. Cet effet intervient lorsque le contenu de la page est affichĂ© avant que les styles ne soient chargĂ©/appliquĂ©. On se retrouve avec un contenu visible, mais qui va subir un nouveau changement une fois la fin du chargement. (On voit souvent cela pour les polices d’écritures personnalisĂ©es qui sont chargĂ©es depuis des serveurs distants).

Pour les webcomponents, cela concerne l’affiche du custom element qui n’a pas encore Ă©tĂ© mis en forme ou dont le contenu n’est pas encore traitĂ© par le javascript encapsulĂ©. Pour Ă©viter ce flash, il existe le selecteur :unresolved .

Exemple :

my-component:unresolved { opacity: 0; }

cachera votre contenu, tant que celui-ci n’est pas initialisĂ©.

Conclusion

La notion de shadow DOM amĂšne de nouvelles problĂšmatiques pour ce qui est de la gestion des styles. S’il n’est pas toujours facile de se rappeler toutes les notations, il est encore plus compliquĂ© de savoir quoi utiliser quand.

A noter aussi que la spec W3C n’est toujours pas en version finale et qu’il est donc possible que cet article ne soit plus exact en cas de changement. Si c’est le cas n’hĂ©sitez pas Ă  commenter đŸ‘‡âŒšïž.

Si l’article vous a plu ❀ ou aidĂ© đŸ€, n’hĂ©sitez pas Ă  👏 😉