Propriétés personnalisées CSS dans la cascade

Publié: 2022-03-10
Résumé rapide ↬ Dans cet article, Miriam approfondit la spécification « Propriétés personnalisées CSS pour les variables en cascade » pour demander : "Pourquoi sont-elles appelées propriétés personnalisées, comment fonctionnent-elles dans la cascade et que pouvons-nous faire d'autre avec elles ? ?" Poussant au-delà de la métaphore « variable », les propriétés personnalisées peuvent fournir de nouvelles façons d'équilibrer le contexte et l'isolement dans les modèles et composants CSS.

Le mois dernier, j'ai eu une conversation sur Twitter à propos de la différence entre les styles "scoped" (générés dans un processus de construction) et les styles "imbriqués" natifs de CSS. J'ai demandé pourquoi, de manière anecdotique, les développeurs évitent la spécificité des sélecteurs d'ID, tout en adoptant les "styles de portée" générés par JavaScript ? Keith Grant a suggéré que la différence réside dans l'équilibre entre la cascade* et l'hérédité, c'est-à-dire en donnant la préférence à la proximité plutôt qu'à la spécificité. Nous allons jeter un coup d'oeil.

La Cascade

La cascade CSS est basée sur trois facteurs :

  1. Importance définie par le drapeau !important et origine du style (utilisateur > auteur > navigateur)
  2. Spécificité des sélecteurs utilisés (inline > ID > class > element)
  3. Ordre des sources du code lui-même (le plus récent a priorité)

La proximité n'est mentionnée nulle part - la relation DOM-tree entre les parties d'un sélecteur. Les paragraphes ci-dessous seront tous deux rouges, même si #inner p décrit une relation plus étroite que #outer p pour le deuxième paragraphe :

Voir le Pen [Cascade : Spécificité vs Proximité](https://codepen.io/smashingmag/pen/OexweJ/) de Miriam Suzanne.

Voir Pen Cascade: Specificity vs Proximity par Miriam Suzanne.
 <section> <p>This text is red</p> <div> <p>This text is also red!</p> </div> </section>
 #inner p { color: green; } #outer p { color: red; }

Les deux sélecteurs ont la même spécificité, ils décrivent tous les deux le même élément p , et aucun n'est signalé comme !important — le résultat est donc basé sur l'ordre de la source uniquement.

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

BEM et styles de portée

Des conventions de nommage telles que BEM ("Block__Element—Modifier") sont utilisées pour garantir que chaque paragraphe est "limité" à un seul parent, évitant complètement la cascade. Les « éléments » de paragraphe reçoivent des classes uniques spécifiques à leur contexte de « bloc » :

Voir le stylo [BEM Selectors & Proximity](https://codepen.io/smashingmag/pen/qzPyeM/) de Miriam Suzanne.

Voir le Pen BEM Selectors & Proximity de Miriam Suzanne.
 <section class="outer"> <p class="outer__p">This text is red</p> <div class="inner"> <p class="inner__p">This text is green!</p> </div> </section>
 .inner__p { color: green; } .outer__p { color: red; }

Ces sélecteurs ont toujours la même importance relative, la même spécificité et le même ordre des sources, mais les résultats sont différents. Les outils CSS « ​​Scoped » ou « modulaires » automatisent ce processus, en réécrivant notre CSS pour nous, sur la base du HTML. Dans le code ci-dessous, chaque paragraphe est limité à son parent direct :

Voir le stylo [Scoped Style Proximity] (https://codepen.io/smashingmag/pen/NZaLWN/) de Miriam Suzanne.

Voir le Pen Scoped Style Proximity de Miriam Suzanne.
 <section outer-scope> <p outer-scope>This text is red</p> <div outer-scope inner-scope> <p inner-scope>This text is green!</p> </div> </section>
 p[inner-scope] { color: green } p[outer-scope] { color: red; }

Héritage

La proximité ne fait pas partie de la cascade, mais elle fait partie du CSS. C'est là que l' héritage devient important. Si nous supprimons le p de nos sélecteurs, chaque paragraphe héritera d'une couleur de son ancêtre le plus proche :

Voir le Pen [Héritage : Spécificité vs Proximité](https://codepen.io/smashingmag/pen/mZBGyN/) de Miriam Suzanne.

Voir l'héritage du stylo : spécificité vs proximité par Miriam Suzanne.
 #inner { color: green; } #outer { color: red; }

Étant donné que #inner et #outer décrivent des éléments différents, notre div et notre section respectivement, les deux propriétés de couleur sont appliquées sans conflit. L'élément p imbriqué n'a pas de couleur spécifiée, donc les résultats sont déterminés par héritage (la couleur du parent direct) plutôt que cascade . La proximité est prioritaire et la valeur #inner remplace la valeur #outer .

Mais il y a un problème : pour utiliser l'héritage, nous stylisons tout à l'intérieur de notre section et div . Nous voulons cibler spécifiquement la couleur du paragraphe.

(Ré-)Introduction des propriétés personnalisées

Les propriétés personnalisées fournissent une nouvelle solution native pour le navigateur ; ils héritent comme n'importe quelle autre propriété, mais ils n'ont pas besoin d'être utilisés là où ils sont définis . En utilisant du CSS simple, sans aucune convention de nommage ni outil de construction, nous pouvons créer un style à la fois ciblé et contextuel, la proximité prenant le pas sur la cascade :

Voir le Pen [Accessoires personnalisés : Spécificité vs Proximité](https://codepen.io/smashingmag/pen/gNGdaO/) de Miriam Suzanne.

Voir le Pen Custom Props: Specificity vs Proximity par Miriam Suzanne.
 p { color: var(--paragraph); } #inner { --paragraph: green; } #outer { --paragraph: red; }

La propriété personnalisée --paragraph hérite de la même manière que la propriété color , mais maintenant nous avons le contrôle exact sur comment et où cette valeur est appliquée. La propriété --paragraph agit comme un paramètre qui peut être transmis au composant p , soit par sélection directe (règles de spécificité) soit par le contexte (règles de proximité).

Je pense que cela révèle un potentiel de propriétés personnalisées que nous associons souvent à des fonctions, des mixins ou des composants.

"Fonctions" et paramètres personnalisés

Les fonctions, les mixins et les composants sont tous basés sur la même idée : un code réutilisable, qui peut être exécuté avec divers paramètres d'entrée pour obtenir des résultats cohérents mais configurables. La distinction est dans ce qu'ils font avec les résultats. Nous allons commencer avec une variable à dégradé rayé, puis nous pourrons l'étendre à d'autres formes :

 html { --stripes: linear-gradient( to right, powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80% ); }

Cette variable est définie sur l'élément html racine (pourrait également utiliser :root , mais cela ajoute une spécificité inutile), donc notre variable rayée sera disponible partout dans le document. Nous pouvons l'appliquer partout où les dégradés sont pris en charge :

Voir le stylo [accessoires personnalisés : variable] (https://codepen.io/smashingmag/pen/NZwrrm/) de Miriam Suzanne.

Voir les accessoires personnalisés du stylo : variable de Miriam Suzanne.
 body { background-image: var(--stripes); }

Ajout de paramètres

Les fonctions sont utilisées comme des variables, mais définissent des paramètres pour modifier la sortie. Nous pouvons mettre à jour notre variable --stripes pour qu'elle ressemble davantage à une fonction en y définissant des variables de type paramètre. Je vais commencer par remplacer to right par var(--stripes-angle) , pour créer un paramètre de changement d'angle :

 html { --stripes: linear-gradient( var(--stripes-angle), powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80% ); }

Il existe d'autres paramètres que nous pourrions créer, en fonction de l'objectif auquel la fonction est censée servir. Devrions-nous permettre aux utilisateurs de choisir leurs propres couleurs de rayures ? Si oui, notre fonction accepte-t-elle 5 paramètres de couleur différents ou seulement 3 qui iront à l'extérieur comme nous l'avons maintenant ? Voulons-nous également créer des paramètres pour les arrêts de couleur ? Chaque paramètre que nous ajoutons offre plus de personnalisation au détriment de la simplicité et de la cohérence.

Il n'y a pas de bonne réponse universelle à cet équilibre - certaines fonctions doivent être plus flexibles et d'autres doivent être plus opiniâtres. Les abstractions existent pour assurer la cohérence et la lisibilité de votre code, alors prenez du recul et demandez quels sont vos objectifs. Qu'est-ce qui doit vraiment être personnalisable et où la cohérence doit-elle être appliquée ? Dans certains cas, il peut être plus utile d'avoir deux fonctions opiniâtres, plutôt qu'une fonction entièrement personnalisable.

Pour utiliser la fonction ci-dessus, nous devons transmettre une valeur au paramètre --stripes-angle et appliquer la sortie à une propriété de sortie CSS, comme background-image :

 /* in addition to the code above… */ html { --stripes-angle: 75deg; background-image: var(--stripes); } 

Voir le stylo [accessoires personnalisés : fonction] (https://codepen.io/smashingmag/pen/BgwOjj/) de Miriam Suzanne.

Voir les accessoires personnalisés du stylo : fonction de Miriam Suzanne.

Hérité contre universel

J'ai défini la fonction --stripes sur l'élément html par habitude. Les propriétés personnalisées héritent et je veux que ma fonction soit disponible partout, il est donc logique de la placer sur l'élément racine. Cela fonctionne bien pour hériter de variables telles que --brand-color: blue , nous pouvons donc également nous attendre à ce que cela fonctionne également pour notre "fonction". Mais si nous essayons à nouveau d'utiliser cette fonction sur un sélecteur imbriqué, cela ne fonctionnera pas :

Voir le stylo [Accessoires personnalisés : Échec de l'héritage de la fonction](https://codepen.io/smashingmag/pen/RzjRrM/) de Miriam Suzanne.

Voir Pen Custom Props: Function Inheritance Fail par Miriam Suzanne.
 div { --stripes-angle: 90deg; background-image: var(--stripes); }

Le nouveau --stripes-angle est entièrement ignoré. Il s'avère que nous ne pouvons pas compter sur l'héritage pour les fonctions qui doivent être recalculées. En effet, chaque valeur de propriété est calculée une fois par élément (dans notre cas, l'élément racine html ), puis la valeur calculée est héritée . En définissant notre fonction à la racine du document, nous ne mettons pas toute la fonction à la disposition des descendants, mais uniquement le résultat calculé de notre fonction.

Cela a du sens si vous l'encadrez en termes de paramètre en cascade --stripes-angle . Comme toute propriété CSS héritée, elle est disponible pour les descendants mais pas pour les ancêtres. La valeur que nous avons définie sur une div imbriquée n'est pas disponible pour une fonction que nous avons définie sur l'ancêtre racine html . Afin de créer une fonction universellement disponible qui recalculera sur n'importe quel élément, nous devons la définir sur chaque élément :

Voir le stylo [accessoires personnalisés : fonction universelle] (https://codepen.io/smashingmag/pen/agLaNj/) de Miriam Suzanne.

Voir les accessoires personnalisés du stylo : fonction universelle de Miriam Suzanne.
 * { --stripes: linear-gradient( var(--stripes-angle), powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80% ); }

Le sélecteur universel rend notre fonction disponible partout, mais nous pouvons la définir plus étroitement si nous le voulons. L'important est qu'il ne peut recalculer que là où il est explicitement défini. Voici quelques alternatives :

 /* make the function available to elements with a given selector */ .stripes { --stripes: /* etc… */; } /* make the function available to elements nested inside a given selector */ .stripes * { --stripes: /* etc… */; } /* make the function available to siblings following a given selector */ .stripes ~ * { --stripes: /* etc… */; } 

Voir le stylo [accessoires personnalisés : fonction étendue] (https://codepen.io/smashingmag/pen/JQMvGM/) de Miriam Suzanne.

Voir Pen Custom Props: Scoped Function de Miriam Suzanne.

Cela peut être étendu avec n'importe quelle logique de sélecteur qui ne repose pas sur l'héritage.

Paramètres libres et valeurs de repli

Dans notre exemple ci-dessus, var(--stripes-angle) n'a ni valeur ni repli. Contrairement aux variables Sass ou JS qui doivent être définies ou instanciées avant d'être appelées, les propriétés personnalisées CSS peuvent être appelées sans jamais être définies. Cela crée une variable "libre", semblable à un paramètre de fonction qui peut être hérité du contexte.

Nous pouvons éventuellement définir la variable sur html ou :root (ou tout autre ancêtre) pour définir une valeur héritée, mais nous devons d'abord considérer la solution de repli si aucune valeur n'est définie. Il y a plusieurs options, selon exactement le comportement que nous voulons

  1. Pour les paramètres "obligatoires", nous ne voulons pas de repli. Telle quelle, la fonction ne fera rien jusqu'à ce que --stripes-angle soit défini.
  2. Pour les paramètres « facultatifs », nous pouvons fournir une valeur de repli dans la fonction var() . Après le nom de la variable, nous ajoutons une virgule, suivie de la valeur par défaut :
 var(--stripes-angle, 90deg)

Chaque fonction var() ne peut avoir qu'une seule solution de secours — donc toute virgule supplémentaire fera partie de cette valeur. Cela permet de fournir des défauts complexes avec des virgules internes :

 html { /* Computed: Hevetica, Ariel, sans-serif */ font-family: var(--sans-family, Hevetica, Ariel, sans-serif); /* Computed: 0 -1px 0 white, 0 1px 0 black */ test-shadow: var(--shadow, 0 -1px 0 white, 0 1px 0 black); }

Nous pouvons également utiliser des variables imbriquées pour créer nos propres règles de cascade, en donnant différentes priorités aux différentes valeurs :

 var(--stripes-angle, var(--global-default-angle, 90deg))
  1. Tout d'abord, essayez notre paramètre explicite ( --stripes-angle );
  2. Se replier sur un "utilisateur par défaut" global ( --user-default-angle ) s'il est disponible ;
  3. Enfin, revenez à notre "usine par défaut" (90deg ).

Voir le stylo [accessoires personnalisés : valeurs de secours] (https://codepen.io/smashingmag/pen/jjGvVm/) de Miriam Suzanne.

Voir les accessoires personnalisés du stylet : valeurs de secours par Miriam Suzanne.

En définissant des valeurs de secours dans var() plutôt qu'en définissant explicitement la propriété personnalisée, nous nous assurons qu'il n'y a aucune restriction de spécificité ou de cascade sur le paramètre. Tous les paramètres *-angle sont "libres" d'être hérités de n'importe quel contexte.

Solutions de repli du navigateur par rapport aux solutions de repli variables

Lorsque nous utilisons des variables, nous devons garder à l'esprit deux chemins de secours :

  1. Quelle valeur doit être utilisée par les navigateurs sans prise en charge des variables ?
  2. Quelle valeur doit être utilisée par les navigateurs prenant en charge les variables, lorsqu'une variable particulière est manquante ou invalide ?
 p { color: blue; color: var(--paragraph); }

Alors que les anciens navigateurs ignoreront la propriété de déclaration de variable et reviendront au blue , les navigateurs modernes liront les deux et utiliseront la dernière. Notre var(--paragraph) n'est peut-être pas défini, mais il est valide et remplacera la propriété précédente, de sorte que les navigateurs prenant en charge les variables reviendront à la valeur héritée ou initiale, comme s'ils utilisaient le mot-clé unset .

Cela peut sembler déroutant au premier abord, mais il y a de bonnes raisons à cela. Le premier est technique : les moteurs de navigateur gèrent la syntaxe invalide ou inconnue au « temps d'analyse » (ce qui se produit en premier), mais les variables ne sont pas résolues avant le « temps de la valeur calculée » (ce qui se produit plus tard).

  1. Au moment de l'analyse, les déclarations avec une syntaxe invalide sont complètement ignorées — se rabattant sur les déclarations précédentes. C'est le chemin que suivront les anciens navigateurs. Les navigateurs modernes prennent en charge la syntaxe variable, de sorte que la déclaration précédente est ignorée à la place.
  2. Au moment de la valeur calculée, la variable est compilée comme non valide, mais il est trop tard — la déclaration précédente a déjà été rejetée. Selon la spécification, les valeurs de variables invalides sont traitées de la même manière que unset :

Voir le stylo [Accessoires personnalisés : non valides/non pris en charge contre non définis] (https://codepen.io/smashingmag/pen/VJMGbJ/) de Miriam Suzanne.

Voir les accessoires personnalisés du stylet : non valides/non pris en charge ou non définis par Miriam Suzanne.
 html { color: red; /* ignored as *invalid syntax* by all browsers */ /* - old browsers: red */ /* - new browsers: red */ color: not a valid color; color: var(not a valid variable name); /* ignored as *invalid syntax* by browsers without var support */ /* valid syntax, but invalid *values* in modern browsers */ /* - old browsers: red */ /* - new browsers: unset (black) */ --invalid-value: not a valid color value; color: var(--undefined-variable); color: var(--invalid-value); }

C'est également bon pour nous en tant qu'auteurs, car cela nous permet de jouer avec des solutions de repli plus complexes pour les navigateurs qui prennent en charge les variables et de fournir des solutions de repli simples pour les navigateurs plus anciens. Mieux encore, cela nous permet d'utiliser l'état null / undefined pour définir les paramètres requis. Cela devient particulièrement important si nous voulons transformer une fonction en un mixin ou un composant.

Propriété personnalisée "Mixins"

Dans Sass, les fonctions renvoient des valeurs brutes, tandis que les mixins renvoient généralement une sortie CSS réelle avec des paires propriété-valeur. Lorsque nous définissons une propriété --stripes universelle, sans l'appliquer à une sortie visuelle, le résultat ressemble à une fonction. Nous pouvons faire en sorte que cela se comporte davantage comme un mixin, en définissant également la sortie de manière universelle :

 * { --stripes: linear-gradient( var(--stripes-angle), powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80% ); background-image: var(--stripes); }

Tant que --stripes-angle reste invalide ou indéfini, le mixin ne se compile pas et aucune background-image ne sera appliquée. Si nous définissons un angle valide sur n'importe quel élément, la fonction calculera et nous donnera un arrière-plan :

 div { --stripes-angle: 30deg; /* generates the background */ }

Malheureusement, cette valeur de paramètre héritera , donc la définition actuelle crée un arrière-plan sur le div et tous les descendants . Pour résoudre ce problème, nous devons nous assurer que la valeur --stripes-angle n'hérite pas, en la laissant à la valeur initial (ou à toute valeur invalide) sur chaque élément. On peut faire ça sur le même sélecteur universel :

Voir le Pen [Accessoires personnalisés : Mixin](https://codepen.io/smashingmag/pen/ZdXMJx/) de Miriam Suzanne.

Voir les accessoires Pen Custom : Mixin de Miriam Suzanne.
 * { --stripes-angle: initial; --stripes: /* etc… */; background-image: var(--stripes); }

Styles en ligne sécurisés

Dans certains cas, nous avons besoin que le paramètre soit défini dynamiquement depuis l'extérieur du CSS, sur la base des données d'un serveur back-end ou d'un framework front-end. Avec les propriétés personnalisées, nous pouvons définir en toute sécurité des variables dans notre code HTML sans nous soucier des problèmes de spécificité habituels :

Voir le stylet [Accessoires personnalisés : Mixin + Style en ligne](https://codepen.io/smashingmag/pen/qzPMPv/) de Miriam Suzanne.

Voir les accessoires personnalisés du stylet : Mixin + Inline Style par Miriam Suzanne.
 <div>...</div>

Les styles en ligne ont une grande spécificité et sont très difficiles à remplacer — mais avec les propriétés personnalisées, nous avons une autre option : l'ignorer. Si nous définissons la div sur background-image: none (par exemple), cette variable en ligne n'aura aucun impact. Pour aller encore plus loin, on peut créer une variable intermédiaire :

 * { --stripes-angle: var(--stripes-angle-dynamic, initial); }

Nous avons maintenant la possibilité de définir --stripes-angle-dynamic dans le HTML, ou de l'ignorer, et de définir --stripes-angle directement dans notre feuille de style.

Voir le Pen [Accessoires personnalisés : Mixin + Inline / Override](https://codepen.io/smashingmag/pen/ZdXMao/) de Miriam Suzanne.

Voir les accessoires personnalisés du stylet : Mixin + Inline / Override de Miriam Suzanne.

Valeurs prédéfinies

Pour les valeurs plus complexes ou les modèles courants que nous souhaitons réutiliser, nous pouvons également fournir quelques variables prédéfinies parmi lesquelles choisir :

 * { --tilt-down: 6deg; --tilt-up: -6deg; }

Et utilisez ces préréglages, plutôt que de définir directement la valeur :

 <div>...</div> 

Voir le Pen [Accessoires personnalisés : Mixin + Presets](https://codepen.io/smashingmag/pen/LKemZm/) de Miriam Suzanne.

Voir les accessoires personnalisés du stylet : Mixin + Presets par Miriam Suzanne.

C'est idéal pour créer des tableaux et des graphiques basés sur des données dynamiques, ou même pour créer un agenda.

Voir le Pen [Bar chart in CSS grid + variables](https://codepen.io/smashingmag/pen/wLrEyg/) de Miriam Suzanne.

Voir le graphique Pen Bar dans CSS grid + variables par Miriam Suzanne.

Composants contextuels

Nous pouvons également recadrer notre "mixin" en tant que "composant" en l'appliquant à un sélecteur explicite et en rendant les paramètres facultatifs. Plutôt que de compter sur la présence ou l'absence de --stripes-angle pour basculer notre sortie, nous pouvons compter sur la présence ou l'absence d'un sélecteur de composant. Cela nous permet de définir des valeurs de repli en toute sécurité :

Voir le stylo [accessoires personnalisés : composant] (https://codepen.io/smashingmag/pen/QXqVmM/) de Miriam Suzanne.

Voir le Pen Custom Props: Component de Miriam Suzanne.
 [data-stripes] { --stripes: linear-gradient( var(--stripes-angle, to right), powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80% ); background-image: var(--stripes); }

En plaçant la solution de secours dans la fonction var() , nous pouvons laisser --stripes-angle indéfini et "libre" d'hériter d'une valeur extérieure au composant. C'est un excellent moyen d'exposer certains aspects d'un style de composant à une entrée contextuelle. Même les styles "scoped" générés par un framework JS (ou scoped à l'intérieur du shadow-DOM, comme les icônes SVG) peuvent utiliser cette approche pour exposer des paramètres spécifiques à une influence extérieure.

Composants isolés

Si nous ne voulons pas exposer le paramètre pour l'héritage, nous pouvons définir la variable avec une valeur par défaut :

 [data-stripes] { --stripes-angle: to right; --stripes: linear-gradient( var(--stripes-angle, to right), powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80% ); background-image: var(--stripes); }

Ces composants fonctionneraient également avec une classe ou tout autre sélecteur valide, mais j'ai choisi l'attribut data- pour créer un espace de noms pour tous les modificateurs que nous voulons :

 [data-stripes='vertical'] { --stripes-angle: to bottom; } [data-stripes='horizontal'] { --stripes-angle: to right; } [data-stripes='corners'] { --stripes-angle: to bottom right; } 

Voir le stylo [accessoires personnalisés : composants isolés] (https://codepen.io/smashingmag/pen/agLaGX/) de Miriam Suzanne.

Voir les accessoires personnalisés du stylo : composants isolés par Miriam Suzanne.

Sélecteurs et paramètres

J'aimerais souvent pouvoir utiliser des attributs de données pour définir une variable - une fonctionnalité prise en charge par la spécification CSS3 attr() , mais pas encore implémentée dans aucun navigateur (voir l'onglet ressources pour les problèmes liés sur chaque navigateur). Cela nous permettrait d'associer plus étroitement un sélecteur à un paramètre particulier :

 <div data-stripes="30deg">...</div> /* Part of the CSS3 spec, but not yet supported */ /* attr( , ) */ [data-stripes] { --stripes-angle: attr(data-stripes angle, to right); } <div data-stripes="30deg">...</div> /* Part of the CSS3 spec, but not yet supported */ /* attr( , ) */ [data-stripes] { --stripes-angle: attr(data-stripes angle, to right); } <div data-stripes="30deg">...</div> /* Part of the CSS3 spec, but not yet supported */ /* attr( , ) */ [data-stripes] { --stripes-angle: attr(data-stripes angle, to right); }

En attendant, nous pouvons réaliser quelque chose de similaire en utilisant l'attribut style :

Voir le stylo [accessoires personnalisés : sélecteurs de style] (https://codepen.io/smashingmag/pen/PrJdBG/) de Miriam Suzanne.

Voir les accessoires personnalisés pour stylos : sélecteurs de style par Miriam Suzanne.
 <div>...</div> /* The `*=` atttribute selector will match a string anywhere in the attribute */ [style*='--stripes-angle'] { /* Only define the function where we want to call it */ --stripes: linear-gradient(…); }

Cette approche est particulièrement utile lorsque nous souhaitons inclure d'autres propriétés en plus du paramètre défini. Par exemple, la définition d'une zone de grille peut également ajouter un rembourrage et un arrière-plan :

 [style*='--grid-area'] { background-color: white; grid-area: var(--grid-area, auto / 1 / auto / -1); padding: 1em; }

Conclusion

Lorsque nous commençons à assembler toutes ces pièces, il devient clair que les propriétés personnalisées vont bien au-delà des cas d'utilisation de variables courantes que nous connaissons. Nous sommes non seulement capables de stocker des valeurs et de les étendre à la cascade, mais nous pouvons également les utiliser pour manipuler la cascade de nouvelles manières et créer des composants plus intelligents directement dans CSS.

Cela nous oblige à repenser de nombreux outils sur lesquels nous nous appuyions dans le passé, des conventions de nommage telles que SMACSS et BEM, aux styles « scoped » et CSS-in-JS. Beaucoup de ces outils permettent de contourner la spécificité ou de gérer des styles dynamiques dans une autre langue - des cas d'utilisation que nous pouvons désormais traiter directement avec des propriétés personnalisées. Les styles dynamiques que nous avons souvent calculés en JS peuvent désormais être gérés en transmettant des données brutes au CSS.

Au début, ces changements peuvent être considérés comme une "complexité supplémentaire" - puisque nous ne sommes pas habitués à voir la logique à l'intérieur du CSS. Et, comme pour tout code, une ingénierie excessive peut être un réel danger. Mais je dirais que dans de nombreux cas, nous pouvons utiliser ce pouvoir non pas pour ajouter de la complexité, mais pour déplacer la complexité des outils et conventions tiers, vers le langage de base de la conception Web et (plus important encore) vers le navigateur. Si nos styles nécessitent un calcul, ce calcul doit vivre à l'intérieur de notre CSS.

Toutes ces idées peuvent être poussées beaucoup plus loin. Les propriétés personnalisées commencent à peine à être adoptées plus largement, et nous avons seulement commencé à effleurer la surface de ce qui est possible. Je suis ravi de voir où cela va et ce que les gens proposent d'autre. S'amuser!

Lectures complémentaires

  • "Il est temps de commencer à utiliser les propriétés personnalisées CSS", Serg Hospodarets
  • "Un guide stratégique pour les propriétés personnalisées CSS", Michael Riethmuller