CSS et factorisation du code

Objectifs

On parle souvent en informatique de factorisation du code, c'est à dire le fait d'éviter de répéter plusieurs fois le même code à divers endroits d'un projet. En programmation, cela se traduit par l'utilisation de fonctions (au sens large) qui permettent d'exécuter des tâches quand on en a besoin, en prenant évetuellement des paramètres en argument.

Pour les feuilles de style CSS, on va essayer de même de ne pas répéter du code CSS plusieurs fois. Il n'est pas possible ici de faire des fonctions puisque CSS n'est pas un langage de programmation qui sera exécuté. Cependant il existe des moyens de rendre son code CSS plus concis et d'éviter les répétitions de multiples règles. Voyons donc comment réaliser cela.

Factorisation : quelle feuille de style pour un site complet ? #

Un fichier CSS par page : l'erreur du débutant

Lorsqu'on débute en HTML/CSS, on a tendance à imaginer qu'il faut avoir un fichier CSS pour chaque page HTML. C'est effectivement ce que l'on a fait pour presque tous les TPs... mais uniquement parce que chaque exercice ne contenait qu'une seule page !

Lorsque l'on commence à créer un site constitué de plusieurs pages, une partie de la mise en forme de chaque page est en général identique, par exemple la bannière de haut de page, les polices de caractères utilisées pour les différentes parties des pages, les styles des paragrpahes, des liens, etc. Faire une feuille de style par page implique alors de recopier dans chaque fichier toutes les règles qui servent sur chaque page. Exactement ce qu'on veut en général éviter en informatique. Et dans ce cas il faut changer/ajouter des règles dans chaque fichier à chaque fois qu'on modifie une partie commune, ce qui risque fort d'engendrer des erreurs et/ou oublis.

Ne pas répéter les styles communs

Pour éviter cette répétition des styles communs à toutes les pages du site, il suffit de créer une unique feuille de style pour tout le site. Il est alors possible que le fichier devienne assez long. Il est alors essentiel de bien organiser son contenu et de le commenter, par exemple en organisant comme ci-dessous, en supposant qu'on fait un site sur les instruments de musique :


/* ----------- style communs à toutes les pages ----------- */

/* styles généraux */
body {
    font-family: Arial, sans-serif;
    font-size: 1.2em
    ...
}
...

/* main de la page et son contenu */
main {
    ...
}
...

/* ----------- page saxophone ----------- */
.saxo-details {
    ...
}

/* ----------- page guitare ----------- */
.guitare-details {
    ...
}
                

Mais alors comment différencier les pages ?

La solution ci-dessus pose potentiellement un gros problème : comment faire si on veut par styler des choses différemment dans la page saxophone et guitare ?

On pourrait créer des classes pour différencier le style de certaines parties sur les pages, par exemple :


/* style pour le main de la page saxophone */
main.saxophone {
    ...
}
                

Cette solution n'est pas optimale et en général pas bonne, elle peut être utilisée à la marge (si seulement quelques éléments sont à changer) mais par exemple si on voulait styler différemment les liens sur les différentes pages, ce ne serait pas pratique de mettre une classe à chaque lien.

Alors quelle solution ?

Factoriser le commun et différencier les pages

La solution la plus courante consiste à appeler deux fichiers CSS pour chaque page du site :

  • un fichier CSS pour les styles communs à tout le site
  • un fichier CSS pour les styles spécifiques à chaque page

Pour chaque page on appelle alors 2 fichiers CSS. Par exemple pour la page sur le saxophone :


<link rel="stylesheet" type="text/css" href="styles/communs.css" />
<link rel="stylesheet" type="text/css" href="styles/saxophone.css" />
                

et dans la page sur la guitare :


<link rel="stylesheet" type="text/css" href="styles/communs.css" />
<link rel="stylesheet" type="text/css" href="styles/guitare.css" />
                

On pourrait alors avoir par exemple dans ces fichiers :


/* fichier communs.css */
/* on met ici tous les styles communs à toutes les pages */
body {
    ...
}
...
            

/* fichier saxophone.css */
/* on met ici tous les styles utilisés uniquement sur la page saxophone */
/* Remarque : comme précisé en cours je n'utilise pas le sélecteur header
    pour la bannière car il est trop générique : il peut y avoir plusieurs
    header dans la page => utiliser une classe */
.banniere {
    /* par exemple on veut utiliser une image différente pour la bannière */
    background-image: url("../ui/banniere-saxo.jpg");
}
...
            

/* fichier guitare.css */
/* on met ici tous les styles utilisés uniquement sur la page guitare */
.banniere {
    /* par exemple on veut utiliser une image différente pour la bannière */
    background-image: url("../ui/banniere-guitare.jpg");
}
...
            

Cette solution nous permet de résoudre notre premier problème et d'éviter de recopier de nombreuses règles dans de nombreux fichiers. Allons un peu plus loin cependant et voyons quelques exemples pour factoriser encore dans notre CSS.

Factorisation : comment ne pas répéter des règles ? #

Factoriser pour un même sélecteur dans différentes pages

Nous avons vu comment mettre les règles du site dans une CSS et les règles d'une page donnée dans une 2ème CSS. Mais comment faire si par exemple pour l'élément body on veut avoir des règles pour tout le site mais quelques différences sur chaque page ? On pourra utiliser le même sélecteur et spécifier dans la CSS commune toutes les règles communes et dans les CSS spécifiques mettre seulement les règles de la page donnée. Le navigateur lira alors toutes les règles et les utilisera toutes.

Une règle importante à garder en mémoire : si plusieurs sélecteurs et propriétés identiques apparaissent dans la CSS, c'est la dernière déclaration qui sera utilisée. Pour les autres règles de priorité, il y a pas mal de subtilités qui ont été plus ou abordées en cours. Pour aller plus loin sur ce sujet voir les explications sur la cascade, héritage et spécificité sur le site MDN.

Reprenons l'exemple utilisé plus haut où la bannière de chaque page utilisait une image différente. Toutes les pages auront presque la même bannière, il ne faut donc pas réécrire toutes les propriétés de .banniere dans les CSS de chaque page mais factoriser, par exemple :


/* fichier communs.css */
/* on met ici tous les styles communs à toutes les pages */
...
/* propriétés communes à toutes les bannières des pages */
/* Remarque : comme précisé en cours je n'utilise pas le sélecteur header
    pour la bannière car il est trop générique : il peut y avoir plusieurs
    header dans la page => utiliser une classe */
.banniere {
    max-width: 1200px;
    margin: auto;
    padding: 20px;
    box-sizing: border-box;
    font-size: 1.5em;
    background-position: top left;
    background-size: contain;
    background-repeat: no-repeat;
}
...
                

/* fichier saxophone.css */
/* on met ici tous les styles utilisés uniquement sur la page saxophone */

/* par exemple on veut utiliser une image et une couleur différente  */
.banniere {
    background-image: url("../ui/banniere-saxo.jpg");
    color: red;
}
...
                

/* fichier guitare.css */
/* on met ici tous les styles utilisés uniquement sur la page guitare */

/* par exemple on veut utiliser une image différente pour la bannière */
.banniere {
    background-image: url("../ui/banniere-guitare.jpg");
    color: blue;
}
...
                

Cette solution est simple pour différencier les styles dans des pages différentes. Mais comment factoriser pour des éléments qui apparaissent sur uen même page ?

Factoriser dans un fichier CSS

Prenons l'exemple de paragraphes dans une page. On peut imaginer que tous les paragraphes ont les mêmes styles, sauf certains qui seront sélectionnés via des classes, par exemple avec le code HTML suivant 


<p>Un paragraphe bla bla etc</p>
<p>Un autre paragraphe bli bli etc</p>
<p class="important">Celui-ci est important</p>
<p class="extra">Et ce paragraphe est un extra</p>
<p>Encore un autre paragraphe blo blo etc</p>
                

Comment alors factoriser pour éviter de faire ceci :


/* styles des paragraphes */
p {
    font-family: Times, serif;
    font-size: 1.1em;
    line-spacing: 1.1em;
    text-align: justify;
    padding: 0.5em;
}

/* les paragraphes de classe important */
.important {
    font-family: Times, serif;
    font-size: 1.2em;
    line-spacing: 1.1em;
    text-align: justify;
    padding: 0.5em;
    color: #abcdef;
    padding-left: 1em;
}

/* les paragraphes de classe extra */
.extra {
    font-family: Times, serif;
    font-size: 0.9em;
    line-spacing: 1em;
    text-align: justify;
    padding: 0.5em;
}

                

Il est inutile répéter toutes les propriétés à chaque fois, en particulier pour les propriétés qui sont les mêmes car elles seront appliquées à tous les paragraphes.

Pour les paragraphes avec une classe, lorsque le navigateur voit un paragraphe de classe important alors il applique les styles du sélecteur p (car l'élément est bien un paragraphe) ET ceux du sélecteur .important (car il appartient à cette classe), et combine l'ensemble en utilisant les règles de priorité (voir cascade, héritage et spécificité sur le site MDN). La propriété font-size qui met une taille de 1.1em pour tous les paragraphes est alors écrasée par celle qui figure pour la classe important car son sélecteur est plus spécifique.

On peut donc factoriser comme suit 


/* styles des paragraphes, on met les propriétés communes à tous les p */
p {
    font-family: Times, serif;
    font-size: 1.1em;
    line-spacing: 1.1em;
    text-align: justify;
    padding: 0.5em;
}

/* les paragraphes de classe important */
/* on met les propriétés qui changent le style par défaut
   ET les propriétés en plus */
.important {
    font-size: 1.2em; /* la taille est différente de celle par défaut */
    color: #abcdef;  /* on ajoute une couleur en plus */
    padding-left: 1em; /* on change le padding de gauche, les autres côtés ne changeront pas */
}

/* les paragraphes de classe extra */
/* on met les propriétés qui changent le style par défaut
   ET les propriétés en plus */
.extra {
    font-size: 0.9em; /* la taille est différente de celle par défaut */
    line-spacing: 1em; /* l'espacement des lignes est différente */
}
                

Sur cet exemple simple on passe de 17 lignes ce propriétés à 10 lignes, mais le plus important est que si on a besoin de changer quelque chose alors il suffira de le changer une seule fois et non pas jusqu'à 3 fois sur 3 lignes différentes dans la 1ère version, ce qui ne manquerait pas d'être source d'erreurs et d'incohérences de style.

Conclusion #

Les deux techniques vues ici pour factoriser sont bien sûr à combiner pour faire des CSS plus concises et plus efficaces en termes de quantité de code et surtout de faciliter de maintenance du code en minimisant les changements à réaliser pour faire évoluer le style du site.

Pour aller plus loin :