Organiser un projet CSS
Quelques notes rapides et en vrac sur la façon dont je gère des projets CSS depuis quelques temps. Comme il n'existe pas vraiment de standards dans le domaine, il s'agit ici d'une simple habitude de travail.
Structuration du projet
- Séparer ce qui qui pourrait être utilisé ailleurs de ce qui est spécifique au projet.
- Diviser le code en unités (fichiers) facilitant son utilisation et sa maintenance.
- css
- core
- project
Le dossier core contiendra les règles CSS génériques, qui pourraient être utilisées dans n'importe quel autre projet(1) ; le dossier project contiendra lui les feuilles de styles spécifiques au projet développé.
Règles génériques
La structuration en fichiers dépendra ensuite des habitudes de chacun ou des contraintes du projet lui-même ; il n'y a pas de normes à proprement parlé. On peut par exemple imaginer subdiviser son code en fonction de la nature des règles CSS : un premier fichier pour les règles construisant la maquette, un deuxième pour gérer la typographie et un troisième pour les couleurs. J'ai décidé pour ma part de séparer ces règles applicables à tout type de projet dans trois fichiers :
base.css: règles qui s'appliquent aux éléments HTML directement.common.css: ensemble de classes génériques.important.css: règles qui se surimposent à toutes les autres.
- css
- core
- base.css
- common.css
- important.css
- project
- core
Règles spécifiques
Tout comme pour les règles génériques, les règles propres au projet peuvent être dispatchées dans plusieurs fichiers. L'un d'entre eux devra avoir un rôle particulier : styles.css est construit pour importer tous les autres fichiers nécessaires.
- css
- core
- base.css
- common.css
- important.css
- project
- main.css
- styles.css
- core
/* Import base and common rules */
@import "../core/base.css";
@import "../core/common.css";
/* Import your project rules */
@import "main.css";
/* Import important rules */
@import "../core/important.css";
L'utilisation d'un fichier d'import à plusieurs avantages :
- Comme un unique appel à ce fichier dans un document web met à disposition l'ensemble du projet, nous n'avons plus à craindre de fragmenter le code entre plusieurs fichiers comme bon nous semble.
- Il nous permet de modifier rapidement le rendu CSS dans un document web en important certains fichiers et pas d'autres, sans avoir à toucher au code HTML lui-même.
- Il servira par ailleurs de base à la concaténation de tous ces fichiers en un seul (voir plus bas).
Performances en production : concaténer, minifier et compresser
L'organisation décrite plus haut est acceptable en développement, celui du projet CSS lui même ou celui d'un projet web (site, application) qui l'utilise. Elle n'est cependant pas recommendable en production, où il faut aussi penser à réduire le nombre et la taille des requêtes envoyées au serveur.
- Concaténation de tous les fichiers en un seul.
- Minification du fichier résultant.
- Compression de ce fichier.
La concaténation peut se faire à l'aide d'un script bash, en se basant sur le fichier d'import styles.css ; la compression s'appuiera sur un logiciel comme gzip(2) ; la minification demandera elle un outil plus spécifique. Il en existe plusieurs, codés avec différents langages ; j'utilise pour ma part Yui Compressor.
Script bash de traitement
Voici, à titre d'exemple, une version simplifiée des fonctions bash que j'utilise pour concaténer, minifier et compresser mes projets CSS :
YUI_COMPRESSOR="path/to/yuicompressor-x.x.x.jar"
MIN_EXT=".min"
# Concaténation, minification et compression d'un fichier CSS
# @param {String} $1 Nom/chemin du fichier CSS d'import à traiter
# @param {String] $2 Nom/chemin du fichier compressé à créer.
#
build_css() {
if [[ $1 ]] && [[ $2 ]]; then
concatenate $1 $2
minify css $2
compress $2".min"
fi
}
# Concaténation des fichiers CSS déclarés dans un fichier d'import
# @param {String] $1 Nom/chemin du fichier d'import CSS à concaténer
# @param {String] [$2] Nom/chemin de la version concaténée du fichier
#
concatenate() {
if [[ $1 ]]; then
# Concatener les fichiers
echo -e "[INFO] Concaténation des fichiers css"
local root_path=`dirname $1`"/"
local concatenate_file = $1".concatenate"
if [[ $2 ]]; then
local concatenate_file=$2
fi
while read line
do
local res=$(expr "$line" : '.*"\([^"]\+.css\)";')
if [ "$res" ]; then
echo -e "[INFO] Concaténation de "$res
cat $root_path$res >> $concatenate_file
fi
done < $1
fi
}
# Minification de fichier à partir de yuiCompressor
# @param $1 Type de fichier (css|js)
# @param $2 Nom du fichier source
# @param [$3] Nom du fichier minifié
#
minify() {
if [[ $2 ]]; then
if [[ $3 ]]; then
local file_min=$3
else
local file_min=$2$MIN_EXT
fi
echo -e "[INFO] Minification du fichier "$2
java -jar $YUI_COMPRESSOR $2 --type $1 --line-break 1000 -o $file_min
fi
}
# Fonction de compression d'un fichier au format gzip
# avec extension min
# @param $1 Nom du fichier
#
compress() {
if [ $1 ]; then
local file_name=$1
echo -e "[INFO] Compression du fichier "$file_name
gzip $file_name
if [[ "$?" != 0 ]]; then
echo -e "[ERROR] Erreur lors de la compression gzip du fichier "$file_name
exit
fi
mv $file_name".gz" $file_name
fi
}
Remarques et problématiques
Une remarque concernant le processue de concaténation : nous nous sommes efforcer plus haut de structurer le code dans différents fichiers classés dans une arborescence de répertoires afin de faciliter son organisation et sa maintenance. La concaténation casse cette structuration et, de fait, peut aussi casser certaines règles css, notamment celles faisant appel à des images.
- css
- core
- base.css
- baz.png
- common.css
- important.css
- project
- main.css
- qux.png
- styles.css
- core
.foo {
background-image: url("../core/baz.png");
}
.bar {
background-image: url("qux.png");
}
Une fois les fichiers concaténés, ces règles css utilisant des urls relatives pour définir des images de fond n'auront plus de sens. La mise en production ne se réduira donc pas à concaténer et à compresser les feuilles de styles : il faudra aussi penser à déplacer les éventuelles images utilisées et/ou éditer les fichiers avant concaténation. Si on décide par exemple de déplacer les images à la racine du dossier qui sera mis en production, voilà à quoi pourrait ressembler l'édition des fichiers CSS dans la fonction concatenate :
# cat $root_path$res >> $concatenate_file
sed "s|../core/||g" $root_path$res >> $concatenate_file
En reprenant une structuration de projet décrite par ailleurs, nous obtenons l'organisation suivante :
- css
- build
- baz.png
- qux.png
- styles.css.min
- doc
- css
- core
- base.css
- baz.png
- common.css
- important.css
- project
- main.css
- qux.png
- styles.css
- core
- test
- build.sh
- LICENCE
- README
- build
Le dossier css/css serait le dossier de travail (de développement) et le dossier css/build serait celui déployé en production.