Avantages de l'utilisation de propriétés personnalisées dans les systèmes de conception et les bibliothèques de composants
Je m'appelle Dave et je suis développeur front-end senior chez Nordhealth. Je travaille sur la conception et le développement de notre système de conception Nord, qui comprend la création de composants Web pour notre bibliothèque de composants. Je voulais vous expliquer comment nous avons résolu les problèmes liés au style des composants Web à l'aide des propriétés personnalisées CSS, ainsi que certains des autres avantages de l'utilisation de ces propriétés dans les systèmes de conception et les bibliothèques de composants.
Comment nous créons des composants Web
Pour créer nos composants Web, nous utilisons Lit, une bibliothèque qui fournit une grande quantité de code récurrent comme l'état, les styles cloisonnés, les modèles, etc. Lit est non seulement léger, mais il est également basé sur des API JavaScript natives. Nous pouvons ainsi fournir un bundle de code allégé qui exploite les fonctionnalités déjà disponibles dans le navigateur.
Mais l'aspect le plus intéressant des composants Web est qu'ils fonctionnent avec presque tous les frameworks JavaScript existants, voire sans framework du tout. Une fois le package JavaScript principal référencé dans la page, l'utilisation d'un composant Web ressemble beaucoup à celle d'un élément HTML natif. Le seul signe révélateur qu'il ne s'agisse pas d'un élément HTML natif est le tiret cohérent dans les tags. Il s'agit d'une norme pour indiquer au navigateur qu'il s'agit d'un composant Web.
Encapsulation de style Shadow DOM
Tout comme les éléments HTML natifs disposent d'un Shadow DOM, les Web Components en ont également un. Le Shadow DOM est un arbre de nœuds masqué dans un élément. Le meilleur moyen de visualiser cela est d'ouvrir l'outil d'inspection du navigateur et d'activer l'option "Afficher l'arborescence DOM de l'ombre". Une fois que vous avez fait cela, essayez d'examiner un élément de saisie natif dans l'inspecteur. Vous pouvez maintenant ouvrir cette saisie et voir tous les éléments qu'elle contient. Vous pouvez même essayer cela avec l'un de nos composants Web. Essayez d'inspecter notre composant d'entrée personnalisée pour voir son Shadow DOM.
L'encapsulation du style est l'un des avantages (ou des inconvénients, selon vos perspectives) de Shadow DOM. Si vous écrivez du code CSS dans votre composant Web, ces styles ne peuvent pas s'afficher, ni affecter la page principale ni les autres éléments. Ils sont entièrement contenus dans le composant. En outre, le code CSS écrit pour la page principale ou un composant Web parent ne peut pas s'infiltrer dans votre composant Web.
Cette encapsulation des styles est un avantage de notre bibliothèque de composants. Cela nous garantit davantage que lorsqu'un utilisateur utilise l'un de nos composants, son apparence sera conforme à nos attentes, quels que soient les styles appliqués à la page parent. Pour nous en assurer, nous ajoutons all: unset;
à la racine, ou "hôte", de tous nos composants Web.
Toutefois, que se passe-t-il si une personne utilisant votre composant Web a une raison légitime de modifier certains styles ? Peut-être qu'une ligne de texte doit être plus contrastée en raison de son contexte, ou qu'une bordure doit être plus épaisse ? Si aucun style ne peut être appliqué à votre composant, comment pouvez-vous déverrouiller ces options de style ?
C'est là que les propriétés personnalisées CSS entrent en jeu.
Propriétés CSS personnalisées
Les propriétés personnalisées portent un nom très approprié : il s'agit de propriétés CSS que vous pouvez nommer entièrement vous-même et auxquelles vous pouvez appliquer la valeur souhaitée. La seule exigence est de les préfixer de deux traits d'union. Une fois que vous avez déclaré votre propriété personnalisée, la valeur peut être utilisée dans votre CSS à l'aide de la fonction var()
.
En ce qui concerne l'héritage, toutes les propriétés personnalisées sont héritées, ce qui suit le comportement typique des propriétés et valeurs CSS standards. Toute propriété personnalisée appliquée à un élément parent, ou à l'élément lui-même, peut être utilisée comme valeur dans d'autres propriétés. Nous utilisons de manière intensive les propriétés personnalisées pour nos jetons de conception en les appliquant à l'élément racine via notre framework CSS. Cela signifie que tous les éléments de la page peuvent utiliser ces valeurs de jeton, qu'il s'agisse d'un composant Web, d'une classe d'assistance CSS ou d'un développeur souhaitant extraire une valeur de notre liste de jetons.
Cette possibilité d'hériter des propriétés personnalisées, à l'aide de la fonction var()
, nous permet de parcourir le Shadow DOM de nos composants Web et de donner aux développeurs un contrôle plus précis pour styliser nos composants.
Propriétés personnalisées dans un composant Web Nord
Lorsque nous développons un composant pour notre système de conception, nous adoptons une approche réfléchie de son CSS. Nous voulons un code allégé, mais facile à gérer. Les jetons de conception dont nous disposons sont définis en tant que propriétés personnalisées dans notre framework CSS principal sur l'élément racine.
Ces valeurs de jetons sont ensuite référencées dans nos composants. Dans certains cas, nous appliquerons la valeur directement à la propriété CSS, mais dans d'autres, nous définirons une nouvelle propriété personnalisée contextuelle et y appliquerons la valeur.
Nous allons également extraire certaines valeurs spécifiques au composant, mais qui ne figurent pas dans nos jetons, et les transformer en propriété personnalisée contextuelle. Les propriétés personnalisées adaptées au composant présentent deux avantages principaux. Tout d'abord, cela signifie que notre code CSS peut être plus "sec" puisque cette valeur peut être appliquée à plusieurs propriétés au sein du composant.
Deuxièmement, cela permet de modifier de manière très claire l'état et les variations du composant. Seule la propriété personnalisée doit être modifiée pour mettre à jour toutes ces propriétés lorsque, par exemple, vous stylisez un état de survol ou actif, ou, dans ce cas, une variation.
Toutefois, le plus grand avantage est que lorsque nous définissons ces propriétés personnalisées contextuelles sur un composant, nous créons pour chacun de nos composants une sorte d'API CSS personnalisée. L'utilisateur de ce composant peut alors appuyer dessus.
L'exemple précédent montre l'un de nos composants Web avec une propriété personnalisée contextuelle modifiée par le biais d'un sélecteur. Le résultat de cette approche est un composant qui offre à l'utilisateur une flexibilité suffisante pour le style, tout en gardant la plupart des styles réels sous contrôle. De plus, en bonus, les développeurs de composants peuvent intercepter les styles appliqués par l'utilisateur. Si nous souhaitons ajuster ou étendre l'une de ces propriétés, nous pouvons le faire sans que l'utilisateur ait à modifier son code.
Nous trouvons cette approche extrêmement efficace, non seulement pour nous en tant que créateurs de nos composants de système de conception, mais aussi pour notre équipe de développement lorsqu'elle utilise ces composants dans nos produits.
Aller plus loin avec les propriétés personnalisées
Au moment de la rédaction de ce document, nous ne dévoilons pas ces propriétés personnalisées contextuelles dans notre documentation. Toutefois, nous prévoyons de le faire pour que notre équipe de développement puisse les comprendre et les exploiter. Nos composants sont empaquetés sur npm avec un fichier manifeste qui contient tout ce qu'il y a à savoir à leur sujet. Nous utilisons ensuite le fichier manifeste en tant que données lorsque notre site de documentation est déployé, ce qui se fait à l'aide d'Eleventy et de sa fonctionnalité de données globales. Nous prévoyons d'inclure ces propriétés personnalisées contextuelles dans ce fichier de données manifeste.
Nous souhaitons également améliorer la façon dont ces propriétés personnalisées contextuelles héritent des valeurs. Actuellement, par exemple, si vous souhaitez ajuster la couleur de deux composants de séparation, vous devez cibler ces deux composants spécifiquement avec des sélecteurs ou appliquer la propriété personnalisée directement sur l'élément avec l'attribut de style. Cela peut sembler acceptable, mais il serait plus utile que le développeur puisse définir ces styles sur un élément contenant ou même au niveau racine.
Vous devez définir la valeur de la propriété personnalisée directement sur le composant, car nous les définissons sur le même élément via le sélecteur d'hôte du composant. Les jetons de conception globaux que nous utilisons directement dans le composant sont transmis directement, ne sont pas affectés par ce problème et peuvent même être interceptés sur les éléments parents. Comment tirer le meilleur parti des deux mondes ?
Propriétés personnalisées privées et publiques
Les propriétés personnalisées privées ont été élaborées par Lea Verou. Il s'agit d'une propriété personnalisée contextuelle "privée" pour le composant lui-même, mais définie sur une propriété personnalisée "publique" avec une création de remplacement.
En définissant nos propriétés personnalisées contextuelles de cette manière, nous pouvons toujours effectuer toutes les opérations que nous effectuions auparavant, comme hériter des valeurs de jeton globales et réutiliser des valeurs dans le code de notre composant. Toutefois, le composant héritera également de nouvelles définitions de cette propriété sur lui-même ou sur n'importe quel élément parent.
Bien que l'on puisse affirmer que cette méthode n'est pas vraiment "privée", nous pensons qu'il s'agit d'une solution plutôt intéressante à un problème qui nous inquiète. Lorsque nous en aurons l'occasion, nous allons résoudre ce problème dans nos composants afin que notre équipe de développement puisse mieux contrôler l'utilisation des composants tout en bénéficiant des garde-fous que nous avons mis en place.
J'espère que cet aperçu de la façon dont nous utilisons les Web Components avec les propriétés CSS personnalisées vous a été utile. Dites-nous ce que vous en pensez et si vous décidez d'utiliser l'une de ces méthodes dans votre propre travail, n'hésitez pas à me contacter sur Twitter (@DavidDarnes). Vous pouvez également retrouver Nordhealth sur Twitter : @NordhealthHQ, ainsi que le reste de mon équipe, qui a travaillé dur pour rassembler ce système de conception et implémenter les fonctionnalités mentionnées dans cet article : @Viljamis, @WickyNilliams et @eric_habich.
Image principale de Dan Cristian Pădureț