Des propriétés personnalisées très, très, très personnalisées

Retrouvez les slides en ligne sur
ffoodd.fr/paris-web.2020
et en version « commentée »
ffoodd.fr/paris-web.2020/verbose.

Les concepts-clés

À digérer avant de jouer avec les propriétés personnalisées :

  • La cascade ;
  • L’héritage ;
  • Les stratégies de repli ;
  • Les particularités.

Venez, on va bien rigoler !

Si, si, promis…

Cascade

Hop, galipette !

La cascade est l’algorithme décidant quel style est finalement appliqué.

Amelia Wattenberger a rédigé un billet de blog interactif expliquant à merveille la cascade, mais vous pouvez aussi lire la spécification du module Cascading and inheritance du W3C, si vous préférez !

Les propriétés personnalisées respectent la cascade, au même titre que les autres propriétés.

Galipette réalisée par un professionnel, ne tentez pas ça chez vous.

L’héritage (Mitsouko)

C’est comme ça-aaaaaa

L’héritage intervient quand la cascade n’aboutit à aucune valeur — une sombre histoire de succession, donc…

Mais toutes les propriétés ne sont pas héritées ! Ça concerne presque exclusivement les propriétés liées au texte — cependant on peut influer sur ce comportement avec les valeurs inherit, initial, revert ou unset.

Cependant les propriétés personnalisées sont héritées, au cas où elles servent dans des propriétés liées au texte. Intéressant, non ?

Valeur de repli

Quand une valeur n’est pas supportée, elle est ignorée et la cascade puis l’héritage interviennent.

Cascade

                
main {
  color: red;
  color: color(display-p3 1 .5 0);
}
              

Héritage

                
html {
  color: red;
}

main {
  color: color(display-p3 1 .5 0);
}
              

Les particularités

Une sorte de définition

Les propriétés personnalisées :

  • ne servent à rien si elles ne sont pas utilisées dans une valeur !
  • peuvent avoir plusieurs valeurs au même moment, en fonction de l’élément qui s’en sert — cascade, héritage, repli… suivez, un peu !
  • sont déclaratives, comme l’ensemble de CSS : si vous changez une valeur, elle changera pour tout l’arbre du document concerné — indépendamment de la position de la définition de la valeur dans vos CSS ;
  • ne sont pas affectées par la propriéte all.

Rien à voir avec les variables Sass, donc !

Les capacités

Qu’est-ce qu’on va bien pouvoir faire de ça ?

Une palette de couleurs

Sara Soueidan explique l’utilisation des propriétés personnalisées combinées à hsl() pour gérer une palette de couleurs — et Una Kravets pousse la réflexion encore plus loin.

J’ai implémenté une telle palette dans sseeeedd (sur GitHub) :

              
:root {
  --hue: 240;
  --lightness: 30%;
}

main {
  color: hsl(var(--hue), 100%, var(--lightness));
}
            
              
@media screen and (min-width: 32em) {
  :root {
    --lightness: 40%;
  }
}

@media screen and (prefers-color-scheme: dark) {
  :root {
    --lightness: 80%;
  }
}
            

Vous pouvez ainsi changer la palette de couleurs en modifiant la seule propriété personnalisée --hue ou basculer vers une palette complémentaire en modifiant --rotation pour ajouter 180 au lieu de 30.
Vous pouvez aussi augmenter les contrastes et la luminosité en fonction du viewport ou gérer prefers-color-scheme en quelques lignes !

La même méthode peut-être utiliser pour les échelles typographiques : je vous conseille en la matière l’excellent Utopia, par James Gilyead & Trys Mudford, qui propose un générateur mais également des billets de blog extrêmement intéressants.

De la configuration globale

Je s’appelle :root

Vous pouvez utiliser les propriétés personnalisées comme une API de surface avec vos styles :

                
:root {
  --enable-shadows: none;
}

.component {
  box-shadow: var(--enable-shadows, 0 .5rem 1rem black);
}
              
Mark Otto propose des options globales pour (dés)activer des éléments de styles.
                
:root {
  --duration: 1;
}

.foobar {
  transition-duration: calc(var(--duration) * 250ms);
}
              
Hugo Giraudel suggère de réduire les animations globalement en se basant sur une unique propriété personnalisée.

Une cascade personnalisée

Hop, sans les mains

Miriam Suzanne a conceptualisé les cascades personnalisées, une sucession de propriétés personnalisées dans les valeurs de replis de propriétés personnalisées.

                  
button {
  background: var(--btn-bg--state,
    var(--btn-bg,
      var(--btn-bg--type,
        var(--btn-bg--default)
      )
    )
  );
}
            
                  
[disabled] {
  --btn-bg--state: darkgray;
}

.warn {
  --btn-bg--type: maroon;
}
            

Convertir un entier en chaîne

Ce n’est que du CSS

Cassie Evans utilise les compteurs CSS pour afficher un entier dans la propiété content, ce qui n’est pas possible autrement.

                
.icon {
  --number-var: 23;
}

.icon::before {
  counter-reset: number var(--number-var);
  content: counter(number);
}
          

Ce n’est que du CSS, mais y’a quand même des données typées.

De la pseudo-logique

Ce n’est toujours que du CSS

Roman Komarov propose d’utiliser des propriétés personnalisées pseudo-booléennes pour qu’une propriété bascule d’une valeur calculée à une autre. Ana Tudor décortique des exemples concrets de ce qu’elle baptise DRY switching.

              
:root {
  --is-big: 0;
}

.is-big {
  --is-big: 1;
}
            
              
.block {
  padding: calc(
    25px * var(--is-big) +
    10px * (1 - var(--is-big))
  );
}
            

On peut par exemple basculer d’un point à un autre dans un tracé clip-path.

Du responsive sans @media

Alias, ton univers impitoya-ableuh

James Atherton utilise une astuce qu’il nomme space toggler pour se passer des media queries dans son CSS — ou presque.

              
:root {
  --media-lte-sm: initial;
}

div {
  --small-borders: var(--media-lte-sm) 2px;
  border: var(--small-borders, 15px) solid;
}
            
              
@media (min-width: 56.25em) {
  :root {
    --media-lte-sm: ;
  }
}
            

Ce mécanisme vous intrigue ? La spécification du W3C explique très bien ce comportement, et le concept de « valeur invalide garantie » :jetez-y un œil !

En résumé :

  1. la valeur par défaut d’une propriété personnalisée est une valeur invalide garantie — quand vous appelez une propriété personnalisée non déclarée, c’est ce que vous obtenez ;
  2. si la propriété personnalisée est invalide, la propriété entière est invalide au moment du calcul — l;le navigateur ne savait pas qu’il échouerait à calculer, trop tard pour se référer à la cascade…
  3. quand une propriété personnalisée est vide, elle est valide : le navigateur continue comme si de rien n’était ;
  4. pour forcer une propriété personnalisée à être invalide, on utilise le mot-clé initial.

Autrement dit, le space toggler bascule d’une valeur inutile à une valeur invalide pour une propriété personnalisée : quand la valeur est nulle, le navigateur affecte la valeur derrière var() à la propriété personnalisée ; quand elle est invalide, il ignore la propriété personnalisée (elle est donc indéfinie) et utilise la valeur de repli.

Si vous cherchez à vous passer de @media, je vous conseille plutôt la technique des Fab Four par Rémi Parmentier, les modules Grid et Flexbox de CSS, des études de cas par Heydon Pickering et Andy Bell sur every-layout.dev ou encore un exemple détaillé de composant par Geoffrey Crofte (sur CSS-Tricks) notamment.

Styler votre shadow DOM

Une variable pour les gouverner tous, et dans les ténèbres les lier…

Pousser des styles vers le shadow DOM est impossible… mais hériter des propriétés personnalisées fonctionne ! Sarah Dayan donne un exemple de symbôle SVG multicolore conçu pour appeler des propriétés personnalisées définies hors de l’hôte.

              
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
  <symbol id="my-first-icon" viewBox="0 0 20 20">
    <path fill="var(--color-1, red)" d="…"/>
    <path fill="var(--color-2, blue)" d="…"/>
    <path fill="var(--color-3, green)" d="…"/>
  </symbol>
</svg>

<svg class="icon icon-colors">
  <use xlink:href="#my-first-icon"/>
</svg>

            
              
.icon-colors {
  --color-1: #c13127;
  --color-2: #ef5b49;
  --color-3: #cacaea;
}

            

Des styles réactifs

Dans chaarts, je mets en place divers type de graphiques de données en CSS, qui dépendent de propriétés personnalisées déclarées côté HTML.

Autrement dit, ces graphiques réagissent à des changements de données en temps réels si vous mettez à jour les propriétés personnalisées en JavaScript — de préférence à l’aide de la méthode .setProperty.

              
<table class="chaarts radar"
       style="--scale: 20; --step: 5; --items: 7;
       --1: 14; --2: 11; --3: 13; --4: 16; --5: 10;
       --6: 12; --7: 4; --8: var(--1);">
  […]
    <td><span>14</span></td>
  […]
</table>

            
              
.chaarts[class*="radar"] td:nth-of-type(2) span {
  --position: calc(100% - (var(--3) * 100% / (var(--scale) * var(--ratio))));
  clip-path: polygon(
    100% var(--position),
    calc(100% - (var(--2) * 100% / var(--scale))) 100%,
    100% 100%
  );
}

            
Niveau d’intérêt par domaine, sur 20
Accessibilité Référencement Performance Compatibilité Sécurité Qualité de code Test
14 11 13 16 10 12 4

Factoriser votre code

DRY

Certaines valeurs peuvent être très, très longues : les font-family, les images (URL, dégradés, etc.), ou encore les tracés complexes dans polygon().

Si la même valeur à rallonge est utilisée plusieurs fois, simplifiez-vous la vie : utilisez des propriétés personnalisées !

            
:root {
  --stripes: url("data:image/svg+xml,%3Csvg width='6' height='6' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23ffffff99'%3E%3Cpath d='M5 0h1L0 6V5zM6 5v1H5z'/%3E%3C/g%3E%3C/svg%3E");
}

tr:nth-child(odd) {
  background: white var(--stripes);
}

.hero {
  background: rebeccapurple var(--stripes);
}

          

Conclusion

Les fausses bonnes idées

Ça envoie du rêve, mais…

Merci

Et à bientôt

Crédits