Ces derniers temps, nous avons pu observer de nombreuses personnes qui rencontraient des difficultés avec CSS, aussi bien des débutants que des développeurs confirmés. Certains n’apprécient pas la façon dont le langage fonctionne et se demandent s’il ne vaudrait pas mieux remplacer CSS par un autre langage (ce qui a donné naissance aux processeurs CSS) ; d’autres utilisent des frameworks en espérant écrire moins de code (nous avons vu dans un article précédent que, généralement, ce n’était pas le cas). D’autres encore abandonnent CSS pour n’utiliser que JavaScript à des fins de mise en forme.

Toutefois il n’est pas toujours nécessaire d’ajouter un processeur CSS dans vos outils de travail. Nul besoin non plus d’ajouter un framework lourd simplement pour démarrer un projet. Quant à utiliser JavaScript pour faire ce pour quoi CSS a été conçu… c’est simplement une idée épouvantable.

Dans cet article, nous allons présenter quelques conseils et recommandations afin d’écrire du code CSS de meilleure qualité et qui soit plus facile à maintenir, en utilisant des feuilles de style plus courtes, avec moins de règles. Au lieu d’être un fardeau CSS pourra s’avérer réellement utile.

Le sélecteur minimal

CSS est un langage déclaratif dans lequel on définit des règles qui permettent de mettre en forme les éléments du DOM. Dans ce langage, certaines règles sont prioritaires sur d’autres selon l’ordre dans lequel elles sont appliquées. Ainsi, les styles définis dans le document remplacent certaines règles déclarées avant.

Prenons par exemple ces fragments de code CSS et HTML :

<button class="button-warning">

 

.button-warning {
  background: red;
}
button, input[type=submit] {
  background: gray;
}

Bien que la règle .button-warning soit définie avant la règle button.input[type=submit], elle prendra la priorité sur cette seconde. Pourquoi ? Quel critère est utilisé afin de décider que telle règle surcharge telle autre ?

La spécificité

Certains sélecteurs sont plus spécifiques que d’autres. Un sélecteur d’identifiant #id aura la priorité par rapport à un sélecteur de classe .class. Que se passe-t-il lorsqu’on utilise un sélecteur qui est plus spécifique que nécessaire ? Si, par la suite, on a besoin de surcharger ces règles, il faudra utiliser un sélecteur qui soit plus spécifique. Plus tard, s’il faut aller plus loin dans le détail, il faudra… encore ajouter un niveau de spécificité. On a ici une boule de neige qui risque de se transformer en avalanche, impossible à maintenir efficacement.

Aussi, quand vous écrivez vos sélecteurs, demandez-vous toujours : quel est le sélecteur le moins spécifique qui peut fonctionner ici ?

Toutes les règles liées à la spécificité sont officiellement définies dans la spécification du W3C sur les sélecteurs CSS, la meilleure source pour retrouver tous les détails qui touchent aux sélecteurs CSS. Si vous préférez quelque chose de plus abordable, vous pouvez lire cet article sur la spécificité en CSS (N.D.T. ou cette page de MDN disponible en français).

L’ajout de nouvelles règles : c’est pas automatique

Prenons une situation classique : il y a une anomalie dans votre code CSS et vous avez trouvé l’élément du DOM qui a récupéré le mauvais style. Ensuite, vous vous apercevez que celui-ci hérite d’une propriété et que ça n’aurait pas dû être le cas.

N’ajoutez pas plus de CSS. Si l’ajout de règles devient un réflexe, votre base de code grandira un peu plus et les bugs à venir seront encore plus difficiles à diagnostiquer.

Profitez-en pour prendre du recul, utilisez les outils de développement de votre navigateur et inspectez l’élément pour voir toute la cascade. Repérez la règle précise qui applique le style indésirable et modifiez cette règle existante pour qu’elle n’ait aucune conséquence malheureuse.

Avec Firefox, vous pouvez déboguer la cascade en cliquant droit sur un élément d’une page et en sélectionnant l’option « Examiner l’élément ».

La cascade CSS via l'inspecteur

Et voilà la superbe cascade. On peut voir ici toutes les règles qui s’appliquent à un élément et l’ordre dans lequel elles sont appliquées. Les règles en haut sont les plus spécifiques et peuvent surcharger les styles déclarés par ailleurs. On peut également voir que, dans certaines règles, certaines déclarations sont rayées : cela signifie qu’une règle plus spécifique s’applique à cette propriété.

En plus de voir les règles, vous pouvez également les activer et les désactiver ou les modifier pour observer les conséquences à la volée. En bref, un outil plutôt utile pour déboguer !

Le correctif pourra être une modification d’une règle, éventuellement autre part dans la cascade. Ça peut tout à fait être une nouvelle règle. Au moins, vous saurez que c’était la bonne décision à prendre et que cette règle était nécessaire à votre code.

C’est un bon moment pour lire le code et chercher les éléments qui peuvent être refactorés. Bien que CSS ne soit pas un langage de programmation, c’est du code et, à ce titre, il devrait recevoir la même estime que celle accordée au code JavaScript ou Python : il doit être propre, lisible et refactoré lorsque c’est nécessaire.

Si tout est !important, rien n’est !important !

Ceci est implicite après les précédentes recommandations, mais puisque c’est crucial je le souligne ici : n’utilisez pas !important dans votre code.

!important est une fonctionnalité du CSS qui vous permet de briser la cascade. CSS veut dire « Cascading Style Sheets » (N.D.T pour « feuilles de style en cascade »), c’est un indice.

!important est souvent utilisé pour corriger un bug à la hâte sans avoir le temps de corriger la cascade. C’est aussi fréquemment utilisé lorsqu’on inclue un framework CSS avec des règles très spécifiques et qu’il est simplement trop compliqué de les surcharger.

Lorsque vous ajoutez !important à une propriété, le navigateur va ignorer les autre règles avec des spécificités plus grandes. On réalise le problème lorsqu’on ajoute !important à une règle qui en surcharge une autre règle, déjà marquée avec !important. Il existe un cas légitime pour utiliser !important : l’utilisation des outils de développement à des fins de débogages. Parfois, vous avez besoin de trouver les valeurs d’une propriété qui vous permettront de résoudre votre bug. Utiliser !important via les outils de développement en modifiant le CSS à la volée vous permettra de trouver ces valeurs en ignorant la cascade.

Une fois que vous savez quel CSS fonctionnera, vous pouvez retourner à votre code et regarder à quel point de la cascade il est nécessaire d’ajouter ce fragment de CSS.

Le monde ne s’arrête pas à px et %

Travailler avec les unités px (pixels) et % (pourcentages) est assez intuitif, nous allons donc nous concentrer sur les unités moins connues ou moins intuitives.

em et rem

L’unité relative la plus connue est em. 1em est équivalent à la taille de fonte de cet élément.

Imaginons le fragment HTML suivant :

<article>
  <h1>Titre</h1>
  <p>Un Anneau pour les amener tous et dans les ténèbres les lier.</p>
</article>

Et une feuille de style qui contient uniquement cette règle :

article {
  font-size: 1.25em;
}

Par défaut, la plupart des navigateurs appliquent une taille de fonte de 16 pixels à l’élément racine (ce qui peut d’ailleurs être modifié par l’utilisateur, c’est sympa et ça permet d’améliorer l’accessibilité). Le paragraphe de texte dans cet élément article sera probablement rendu à l’écran avec une taille de fonte (la propriété font-size) de 20 pixels (16 * 1.25). Qu’en est-il de h1 ? Pour mieux comprendre ce qui va se passer, ajoutons cette autre règle CSS à la feuille de style :

h1 {
  font-size: 1.25em;
}

Même si, là aussi, c’est 1.25em qui est utilisé, il faut tenir compte du fait que em est lié à l’environnement dans lequel elle est utilisée (on dit également qu’elle se « compose »). Cela signifie que si h1 était un héritier direct de body, par exemple, il aurait une taille de 20 pixels (16*1.25). Cependant, notre h1 est situé à l’intérieur d’un élément qui a une taille de fonte différente de la racine (article). Dans ce cas, le coefficient 1.25 s’utilise avec la taille de fonte donnée à la cascade et h1 sera affiché avec une taille de 25 pixels (16 * 1.25 * 1.25).

Au fait, au lieu de faire toutes ces multiplications successives de tête, vous pouvez utiliser l’onglet [Calculé] dans l’inspecteur d’éléments, il affiche la taille courante et la taille finale en pixels :

La cascade CSS via l'inspecteur

L’unité em est très souple et permet de modifier simplement, voire dynamiquement, les paramètres liés aux tailles d’élément (pas seulement font-size mais aussi line-height ou width)

Si vous appréciez l’aspect relatif de em mais que vous n’aimez pas la composition, vous pouvez utiliser l’unité rem. Celle-ci se comporte em sans composition, les valeurs sont uniquement liées aux tailles des éléments de base.

Si nous modifions notre précédent code CSS pour transformer les em en rem pour l’élément h1 :

article {
  font-size: 1.25em;
}

h1 {
  font-size: 1.25rem;
}

Tous les éléments h1 auront une taille de fonte de 20 pixels (on part du principe que la taille de base est 16 pixels), sans se soucier de savoir si le titre est dans un article ou non.

vw et vh

vw et wh sont des unités liées au viewport (N.D.T. la zone du document visible à l’écran). 1vw correspond à 1% de la largeur de la fenêtre tandis que 1vh correspond à 1% de sa hauteur. Ces unités sont incroyablement utiles si vous avez besoin d’une interface utilisateur qui occupe tout l’écran (par exemple le fond noir translucide d’une fenêtre interstitielle) et qui n’est pas toujours reliée à la taille du body.

Les autres unités

Il existe d’autres unités plus rares ou moins souples que vous rencontrerez forcément un jour. Vous pouvez en lire davantage à leur sujet sur MDN.

Utiliser les boîtes flexibles (flexbox)

Nous avons abordé ce sujet dans un article précédent sur les frameworks CSS. Le module des boîtes flexibles (ou flexbox) simplifie la création de disposition et les opérations d’alignement. Si vous ne connaissez pas les flexbox, n’hésitez pas à lire ce guide introductif.

Et oui, vous pouvez utiliser les flexbox dès aujourd’hui à moins de devoir prendre en charge les anciens navigateurs à des fins professionnelles. Actuellement, les boîtes flexibles sont prises en charge à 94% dans les navigateurs, il est grand temps d’arrêter d’utiliser tous ces div flottants, difficiles à déboguer et à maintenir. N’oubliez pas de garder un œil sur le module Grid en cours d’élaboration, ce module permettra de créer des dispositions en un clin d’œil.

De l’utilisation des processeurs CSS

Les compilateurs CSS comme Sass ou Less sont très populaires parmi les développeurs front-end. Ce sont des outils puissants qui, utilisés à bon escient, permettent de travailler plus efficacement avec CSS.

Attention à l’abus d’imbrication

We don’t need to go deeper

Une fonctionnalité souvent présente dans ces processeurs (ou compilateurs) est l’imbrication de sélecteurs. Ainsi, le code Less suivant :

a {
  text-decoration: none;
  color: blue;

  &.important {
    font-weight: bold;
  }
}

Sera traduit de la façon suivante en CSS :

a {
  text-decoration: none;
  color: blue;
}
a.important {
  font-weight: bold;
}

Cette fonctionnalité permet d’écrire moins de code et de regrouper les règles qui impactent les éléments proches au sein du DOM. C’est plutôt pratique pour le débogage.

Toutefois, on voit parfois des abus liés à cette fonctionnalité et les sélecteurs CSS obtenus finissent par refléter l’ensemble du DOM. Si on a la structure HTML suivante :

<article class="post">
  <header>
    <!-- … -->
    <p>Étiquettes : <a href="…" class="tag">non pertinent</a></p>
  </header>
  <!-- … -->
</article>

On pourra avoir une feuille de style Less comme celle-ci :

article.post {
  // … d'autres règles ici
  header {
    // …
    p {
      // …
      a.tag {
        background: #ff0;
      }
    }
  }
}

L’inconvénient ici, c’est que les sélecteurs obtenus sont extrêmement spécifiques. Or, on a déjà vu avant que c’était quelque chose à éviter. Il existe d’autres inconvénients liés à cette sur-imbrication que j’ai détaillés dans un autre article.

Bref, pour résumer : n’utilisez pas l’imbrication pour générer des règles CSS que vous ne rédigeriez pas vous-même.

Include / Extend ?

Les mixins sont une autre fonctionnalité utile des processeurs CSS et qui permettent de réutiliser des fragments de CSS. Imaginons qu’on veuille mettre en forme des boutons et que la plupart de ces boutons utilise des propriétés CSS simples. On pourrait alors créer un mixin comme celui-ci (rédigé ici en Less) :

.button-base() {
  padding: 1em;
  border: 0;
}

Puis créer la règle suivante qui utilise le mixin :

.button-primary {
  .button-base();
  background: blue;
}

Ceci générera le CSS suivant :

.button-primary {
  padding: 1em;
  border: 0;
  background: blue;
}

Comme vous pouvez le voir, le remaniement de code est très pratique !

En plus de l’inclusion d’un mixin, des options d’extension et d’héritage existent (la terminologie exacte diffère selon l’outil). Il s’agit d’une combinaison de plusieurs sélecteurs dans une unique règle.

Voyons un exemple en utilisant le mixin vu avant : .button-base :

.button-primary {
  &:extend(.button-base)
  background: blue;
}
.button-danger {
  &:extend(.button-base)
  background: red;
}

Ceci serait traduit en :

.button-primary, .button-danger {
  padding: 1em;
  border: 0;
}
.button-primary { background: blue; }.button-danger { background: red; }

Quelques articles sont partisans d’utiliser uniquement include tandis que d’autres préconisent l’usage de extend. En réalité, ils produisent chacun un CSS différent et aucun n’est intrinsèquement faux. Selon votre situation, l’usage de l’un ou de l’autre sera préférable.

Comment savoir lequel choisir ? Encore une fois, la règle générale « l’écrirais-je ainsi à la main ? » s’applique.


J’espère que cela pourra vous aider à réfléchir à votre code CSS et à écrire de meilleures règles. Comme nous l’avons déjà écrit plus haut : le CSS est du code et en tant que tel, il doit recevoir autant d’attention et de soin que le reste de votre base de code. Si vous en prenez soin, vous en tirerez de nombreux avantages.


Belén est ingénieur et développeur au sein de l’équipe Mozilla Developer Relations. Elle s’intéresse aux standards du Web, à la qualité du code, à l’accessibilité et au développement de jeux.