L’approche DRY : don’t repeat yourself

Openweb.eu.org > Articles  > L’approche DRY : don’t repeat yourself

Abstract

DRYdon’t repeat yourself – est un des principes de base en informatique. Les deux principaux outils qui permettent d’éviter de se répéter en informatique sont les variables et les fonctions.

La question se pose alors : comment penser DRY avec un langage qui n’a à la base ni variables ni fonctions ?

Article

Certes les dernières spécification de CSS nous apportent les variables, dans le module « CSS Custom Properties for Cascading Variables Module Level 1 » et cela s’utilise ainsi :

:root {
  --becca-color: #663399;
}
/* dans la CSS */
.main-title {
  color: var(--becca-color);
}

Mais en l’état actuel des choses, l’implémentation de ces dernières est très loin d’être répandue (hormis sur Firefox et Chrome, et encore, pas dans les versions stables actuelles à l’heure de l’écriture de cet article). Sauf à utiliser des polyfills, autant le dire de suite : c’est inutilisable tel quel en production. Histoire de noircir le tableau, les fonctions sont également absentes de CSS. À priori, avoir une approche DRY en CSS n’est pas gagné, et pourtant :

  • on peut déjà commencer à réfléchir et concevoir en DRY en pur CSS ;
  • il est possible d’utiliser des outils qui eux apportent les variables et les fonctions.

DRY en CSS pur

Une première approche

Avoir une approche DRY en CSS pur peut se voir de plusieurs façons.

Tout d’abord, se constituer une CSS de base pour s’éviter de répéter l’agrégation des bonnes pratiques et/ou de vos connaissances à chaque début de projet est déjà un premier bon pas. :)

Ensuite, pour éviter de se répéter, on va chercher en CSS des motifs qui se répètent. En anglais, on appelle cela des patterns. Évidemment, cela dépend du projet. Prenons un exemple simple : les styles des messages d’erreurs d’un formulaire.

En général, ces derniers vont se composer ainsi :

  • un style pour les messages d’erreur de saisie ;
  • un pour les messages de confirmation ;
  • éventuellement un pour les simples informations, etc.

Ce qui nous donnerait :

.message-error {
  border: 1px solid red;
  color: red;
  background: #fff;
  font-weight: bold;
}
.message-ok {
  border: 1px solid green;
  color: green;
  background: #fff;
  font-weight: bold;
}
.message-warn {
  border: 1px solid orange;
  color: orange;
  background: #fff;
  font-weight: bold;
}

Les conseils pour les performances l’indiquaient déjà : autant factoriser les propriétés communes – ce qui revient à chercher ces motifs évoqués plus haut et à les mettre ensemble :

.message-error,
.message-ok,
.message-warn {
  border: 1px solid;
  font-weight: bold;
  background: #fff;
}
.message-error {
  border-color: red;
  color: red;
}
.message-ok {
  border-color: green;
  color: green;
}
.message-warn {
  border-color: orange;
  color: orange;
}

Autre possibilité : en pensant plus « module », on peut se dire ceci : tous sont des messages, seul leur type ou leur fonction diffèrent. Le groupe de propriétés factorisées deviendrait alors :

[class*="message-"] { 
  /* tous les éléments dont l'attribut class contient «message-» */
  border: 1px solid;
  font-weight: bold;
  background: #fff;
}
/* ou simplement .message en répercutant sur la structure */

L’avantage de cette dernière méthode est de prévoir déjà les styles si l’on doit créer un nouveau style de message à l’avenir.

Un des autres avantages d’une approche DRY est d’uniformiser les styles, on limitera ainsi les nombreuses variantes très semblables d’une bordure ou d’un arrondi.

Note : l’approche OOCSS invite également à penser ses feuilles de styles en tant qu’objets. Bien entendu, penser en programmation objet implique une meilleure réutilisabilité de ces derniers, et donc de moins se répéter. :)
L’approche SMACSS invite également à penser en modules. Plus de réutilisabilité est également bon pour ne pas se répéter.

Pousser plus loin la recherche de motifs récurrents

Notre exemple s’appliquait à un module particulier, il est possible d’aller beaucoup plus loin dans l’approche DRY, je prends un cas de figure très concret : je dois pouvoir mettre à jour un site, et certains contenus nécessitent des positionnements qui reviennent souvent. Je pourrais bien créer une classe pour chaque présentation, mais sur un site à gros volume de contenu, la CSS peut très rapidement enfler.

Dans ce cas, je vais ajouter à ma boîte à outils des classes comme :

/* gestion du table-design en CSS */
.row {
  display: table;
  table-layout: fixed;
}
.col {
  display: table-cell;
  vertical-align: top;
}
.col-noalign {
  display: table-cell;
}

.w33  { width: 33.333%; }
.w66  { width: 66.666%; }
/* etc. */

Qui me permettront de construire ces mises en page simples sans recréer à chaque fois des propriétés/classes différentes. Dans ce cas, les CSS éviteront de prendre du poids inutilement.

En termes techniques, ces classes avec une unique propriété sont appelés des « helpers » ou des « classes atomiques » : ce sont des classes avec une unique propriété (« atome » vient du grec atomos, qui veut dire « indivisible »).

Ces atomes sont en fait les plus petits composants de la feuille de styles. Ils ont l’avantage d’être réutilisables de par leur nature et de permettre une bonne factorisation des propriétés. Leur grande force est d’éviter l’augmentation du poids de la CSS à moyen et long termes.

Ils ont également pour conséquence de déplacer la complexité de la CSS vers la structure HTML. Cela peut être très pratique dans certains cas : éviter de mobiliser un développeur front-end pour mettre deux contenus côte à côte est une économie de temps tout à fait appréciable ! :)

Par contre, plus l’intégration devient pointue, plus l’intérêt de reporter la complexité de CSS sur la structure HTML est limité :

  • déjà il faudra quand même mobiliser un développeur front-end pour intégrer des contenus, car il sera le seul à pouvoir garantir que ce soit fait correctement ;
  • ensuite la complexité peut être telle qu’il faille trop de classes atomiques pour obtenir la mise en page souhaitée : autant utiliser un style dédié (sur mesure) dans ce cas.

Note : certains ont même pensé à concevoir des CSS intégralement en classes atomiques [1]. Cette approche extrême est à mon humble avis à oublier : la trop grande granularité des styles rend lourde la maintenance des sites, et les CSS vont enfler. La raison en est très simple, on maltraite bien trop un des principes évoqué dans un des articles précédents : éviter un trop fort couplage entre la structure HTML et la CSS. Au final, la CSS risque également de prendre trop de poids à cause de trop de propriétés factorisées.

C’est à réserver à mon avis à des contenus simples, pas à appliquer à tout sur un site : les templates peuvent devenir très lourds à gérer ainsi. Ajoutons à cela que certaines approches d’intégration ne s’y prêtent pas : typiquement, une intégration mobile-first se marie mal avec trop de styles atomiques, comme cela a été expliqué ci-dessus.

DRY et les pré-processeurs

Les pré-processeurs comme Sass ou LESS permettent d’utiliser des variables ainsi que les fonctions, ils sont donc bien pratiques pour avoir des approches DRY.

Variables

Les variables permettent une bonne uniformisation des styles, ainsi le développeur front-end évitera la multiplication des variantes de couleurs là où une seule couleur devrait être utilisée. Cela permet d’éviter ce genre de catastrophe industrielle : 261 déclarations de bleu pour Facebook, sans compter les nuances. Ces déclarations étaient-elles bien toutes nécessaires ? :)

Voici un exemple en Sass :

$becca : #663399;
h1 {
  color: $becca;
}

Et un en LESS :

@becca : #663399;
h1 {
  color: @becca;
}

Ces codes vont se compiler pour donner la CSS statique suivante :

h1 {
  color: #663399;
}

Évidemment, sur un exemple aussi simple, cela peut prêter à sourire. Mais sur une CSS beaucoup plus longue, cela évitera d’innombrables variantes et cela permettra de fixer quelques conventions qui permettront de maintenir plus aisément les styles.

Fonctions

Sass et LESS proposent des fonctions. En voici un exemple en Sass :

@function em($px, $base: $base-font) {
  @return ($px / $base) * 1em;
}
h1 {
  font-size: em(42);
}

Cela permettra de calculer l’équivalent en em de 42 pixels. En supposant que $base-font soit à 15 pixels, cela donnera 2.8em.

Dans le même genre, il y a la possibilité d’un « mixin », l’idée est sensiblement la même qu’une fonction, toutefois, cela permettra de réutiliser des blocs de règles CSS, exemple en LESS :

.em(@size, @bf: @base-font){
@em: @size / @bf;
font-size: unit(round(@em,2), em);
}
h1 {
  .em(42);
}

Le résultat sera le même que l’exemple précédent, toutefois vous aurez noté la différence de logique : le premier exemple calcule une valeur, le second permet de calculer une valeur et de poser en plus une propriété CSS (ou plusieurs).

Note : pas de jalousie entre les fans de l’un ou de l’autre pré-processeur ! :) Ces exemples ne sont que des exemples donnés à titre indicatif, les fonctions et mixins sont disponibles pour Sass et LESS.

Autres possibilités

Sass et LESS offrent une possibilité très utile pour factoriser vos CSS : l’@extend. Comme son nom l’indique, cela permet d’étendre une autre classe, ce qui donne par exemple en Sass :


.uppercase {
  text-transform: uppercase;
}
h1 {
  @extend .uppercase;
  color: red;
}

Ce qui sera compilé en :


h1, .uppercase {
  text-transform: uppercase;
}
h1 {
  color: red;
}

D’autres possibilités existent, tant avec Sass qu’avec LESS, toutefois, nous ne les aborderons pas ici (les sites officiels respectifs vous donneront toutes ces informations).

Bien entendu, il est capital de ne jamais oublier que ces outils sont là pour générer des CSS, certaines approches DRY avec ces surcouches donnent des résultats très mauvais en CSS. Imaginons un exemple : étendre 200 fois une propriété revient à générer ceci :

.classe,
.autre-classe, 
/* placez ici 198 autres sélecteurs !!! */ {

Là visiblement, nous avons un problème : qui ferait sciemment ce genre de code sans pré-processeur ? Si vous ne le feriez pas en CSS, ne le faites pas avec les pré-processeurs, repensez votre approche ou pensez plus élégamment avec des sélecteurs plus appropriés.

Il est donc important de comprendre qu’avoir une approche DRY avec un pré-processeur doit servir une approche DRY en CSS, et pas générer le contraire : le confort du développeur front-end ne doit pas outrepasser… celui de l’utilisateur du site.

Certains développeurs ont poussé à fond cette approche DRY avec les pré-processeurs et obtiennent des fonctions complexes qui génèrent intégralement certains modules quel que soient les valeurs et/ou le contexte. Toutefois, pour certains modules, les plus honnêtes reconnaissent qu’ils ont parfois utilisé un bazooka pour générer… 4 classes qui se seraient écrites en 1 minute « à la main ». Libre à vous de vous faire votre avis sur la question ! :)

Les post-processeurs pour les variables ?

Là où – comme son nom l’indique – le pré-processeur est là pour générer la CSS en intervenant avant cette dernière, le post-processeur quand à lui intervient après la création de la CSS. Selon les directives qu’on lui a indiquées, il transformera la CSS que le développeur front-end aura écrite.

Une autre possibilité pour utiliser les variables CSS qui seront un standard dans un futur – espérons-le – aussi proche que possible est d’utiliser un post-processeur. Ce dernier va s’occuper de transformer la syntaxe pour l’instant trop futuriste des variables CSS en CSS compréhensible par les navigateurs ne sachant pas lire cette syntaxe.

Pour n’en citer qu’un : Pleeease permet ce genre d’approche, entre autres possibilités très intéressantes.

Des outils statistiques

Si vous souhaitez étudier vos CSS, un outil peut vous montrer des tendances intéressantes : CSS Stats. Sans être l’alpha ni l’oméga de l’approche DRY, cet outil donne des tendances intéressantes sur vos feuilles de styles.

Pour ma part, j’ai pu constater des tendances nettes entre mes dernières feuilles de styles et certaines produites il y a quelques années (comprenez : des CSS moins soignées, et sans avoir des approches DRY à l’esprit). Le nombre de déclarations diminue, les propriétés uniques aussi, le ratio nombre de valeurs uniques/nombres de valeurs totales diminue, etc.

Bien évidemment, l’outil permet d’autres informations (spécificité des sélecteurs, etc.), mais nous éviterons de sortir du cadre de cet article. :)

Conclusion

Il est tout à fait possible de penser DRY en CSS, toutefois cela nécessite de penser en CSS avant tout pour en tirer les avantages. Il est possible d’utiliser des outils comme les post ou les pré-processeurs, mais cela implique un ou plusieurs maillons supplémentaires dans la chaîne de travail, et les connaissances dans l’équipe qui vont avec.

En dehors de toute mode ou dogmatisme, ces outils peuvent apporter des choses intéressantes, si tant est qu’ils soient maniés… par des esprits éclairés. Science sans conscience… vous connaissez la suite, à vous de savoir jusqu’où aller. ;)

Références et compléments

Note

Cet article fait partie de la série « Grands principes de construction moderne de CSS ».

Notes

[1Lire par exemple Dry Principles.

À propos de cet article

Vos commentaires

modération a priori

Attention, votre message n’apparaîtra qu’après avoir été relu et approuvé.

Qui êtes-vous ?
Ajoutez votre commentaire ici

Ce champ accepte les raccourcis SPIP {{gras}} {italique} -*liste [texte->url] <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.

Suivre les commentaires : RSS 2.0 | Atom