[Hugo] Supprimer le CSS inutilisé
Contexte
Il est extrêmement rare aujourd’hui que le design d’un site web ne fasse pas intervenir un framework CSS. Il en existe plusieurs qui sont très reconnus
- Twitter Boostrap
- Bulma
- Meterialize
- Tailwind
- etc.
Chacun de ces frameworks CSS ont un poids qui n’est pas négligeable 1. Ainsi un framework comme Twitter Bootstrap dans sa version 4.1.3 représente:
- 172Ko dans sa version non minifiée (boostrap.css)
- 140Ko dans sa version minififiée (boostrap.min.css)
- 24Ko s’il est compressé en gzip
C’est autant de bande passante qui est consommée par chacun de vos visiteurs.
Il se trouve que vous n’utilisez qu’une infime partie des fonctionnalités de ces frameworks CSS. Aussi une grande partie est inutile. Nous allons voir ici commenter supprimer ce code superflu.
Notre exemple se basera sur le générateur de site statique Hugo, mais il est possible d’adapter ce comportement à d’autre système (PHP, JS, etc.)
Suppression du CSS non utilisé
Identifier les ressources à supprimer
Pour pouvoir supprimer les données inutilisées il vous nous être
nécessaire de connaitre la liste des class
et id
utilisés dans nos
pages HTML.
Pour cela, la version 0.68.0 de Hugo a ajouté l’option de
configuration writeStats
2. Celle-ci permet de générer un fichier
hugo_stats.json
à la racine de votre projet. Dans ce fichier vous
retrouverez des statistiques sur les éléments HTML utilisés dans votre
site, à savoir :
- La liste de toutes les balises HTML utilisées
- La liste de toutes les classes HTML utilisées
- La liste de tous les identifiants HTML utilisés
Ce fichier de statistique nous servira à alimenter l’outil de suppression du CSS en lui instruisant la liste des configurations CSS nécessaire (et donc celles qui sont inutiles).
Voici la configuration à mettre dans votre fichier config.toml
[build]
writeStats = true
À la prochaine génération de votre site (via la commande hugo
sans
argument) vous trouverez votre fichier hugo_stats.json
.
$ hugo
| EN
-------------------+------
Pages | 346
Paginator pages | 1
Non-page files | 2
Static files | 51
Processed images | 0
Aliases | 2
Sitemaps | 1
Cleaned | 0
Total in 366 ms
$ ls -l hugo_stats.json
-rw-r--r-- 1 1000 1000 8194 Apr 17 07:08 hugo_stats.json
Activer la génération post-génération
PurgeCSS
2 est un projet Javascript
disponible à l’installation via
npm
. Celui-ci est responsable de la suppression des ressources inutilisées
dans nos fichiers CSS. Nous allons néanmoins devoir télécharger quelques
prérequis avant de pouvoir l’utiliser.
À la vue, du processus de génération des sites dans hugo il est nécessaire de
modifier un peu l’ordre d’exécution des éléments pour pouvoir utiliser
PurgeCSS
. En effet Hugo doit tous d’abord générer l’ensemble des
statistiques d’utilisation (et donc par extension toutes les pages HTML)
avant de pouvoir exécuter la tâche de PurgeCSS
. Ceci peut être réalisé
via les Pipe
PostProcess
ou PostCSS
de Hugo. Ceux-ci nous permettent
de déporter la génération d’un contenu à la fin du processus de
compilation.
Le composant PostCSS
permettant automatiquement d’exécuter des
commandes postcss
(comme PurgeCSS
), il sera parfaitement adapté
à notre besoin. Nous allons donc commencer par installer la commande
postcss
.
# npm install -g postcss-cli
+ [email protected]
added 152 packages from 157 contributors in 13.205s
Vous avez maintenant accès à la commande postcss
dans votre shell.
Nous allons créer la configuration de cette config pour lui expliquer ce
qu’elle doit faire.
Créer un fichier postcss.config.js
à la racine de votre projet avec ce
contenu
module.exports = {
plugins: [
]
}
Nous pouvons maintenant configurer le layout
de notre site web pour
activer le traitement de nos CSS. Dans votre fichier de template
(themes/blog/layoutS/partials/headers.html
dans mon cas) nous allons
remplacer nos balises d’inclusion CSS. Par exemple avant modification
j’avais cette ligne
<link rel="stylesheet" type="text/css" media="screen" href="{{ .Site.BaseURL }}css/normalize.css" />
Il convient de remplacer cette ligne comme ceci
{{ $css := resources.Get "css/main.css" }}
{{ $css = $css | resources.PostCSS | minify | resources.PostProcess }}
<link rel="stylesheet" type="text/css" media="screen" href="{{ $css.RelPermalink }}" />
Nous demandons ici à Hugo de récupérer le fichier d’asset
css/main.css
, de le faire passer dans l’outil PostCSS
pour
sauvegarder le résultat, le minifier et enfin d’afficher le lien qui a été
généré pour ce fichier.
Note: Que faire si j’ai l’erreur error calling PostCSS: interface conversion: interface is nil ?
Il est possible que vous rencontriez cette erreur à la sauvegarde du
code précédent. En effet la commande resources.Get
qui récupère le
fichier à traiter effectue des recherches dans les dossiers assets/
et non pas dans les dossiers static
où devait se trouver vos fichiers
statiques avant modification.
Il convient donc de créer dossier assets/
dans votre projet (à la base
ou dans le thème), y déplacer vos fichiers CSS à l’intérieur et redémarrer
hugo. Voici les commandes effectuées dans mon cas.
$ mkdir themes/blog/assets
$ mv themes/blog/static/css themes/blog/assets
Supprimer les ressource inutiles avec PurgeCSS
Alors on a bien avancé mais techniquement parlant actuellement nos pages
sont exactement les mêmes qu’avant. Nous allons maintenant supprimer
réellement nos ressources CSS inutiles en ajoutant le plugin purgecss
dans notre configuration.
Modifier votre fichier postcss.config.js
comme ceci
const purgecss = require('@fullhuman/postcss-purgecss')({
content: [ './hugo_stats.json' ],
defaultExtractor: (content) => {
let els = JSON.parse(content).htmlElements;
return els.tags.concat(els.classes, els.ids);
}
});
module.exports = {
plugins: [
require('autoprefixer'),
purgecss
]
};
Ici nous configurons postcss pour lui demander d’exécuter deux plugins PostCSS:
- (autoprefixer)[https://github.com/postcss/autoprefixer]
- (purgecss)[https://github.com/FullHuman/purgecss]
Vous devez constater que vous rencontrez une erreur, car ces deux modules npm ne sont pas encore installé. Nous pouvons ajouter ceci dans notre fichier package.json
$ npm init # Si vous n'avez pas de package.json
$ npm install autoprefixer
$ npm install fullhuman/postcss-purgecss
Et voilà! Les directives CSS superflues pour votre site ont été supprimés.
Aller plus loin
Activer uniquement pour la production
Le temps de génération des CSS peut être un peu long avec de gros frameworks. Si vous souhaitez générer les fichiers CSS purgés uniquement pour la production vous pouvez modifier vos templates comme ceci
{{ $css := resources.Get "css/normalize.css" }}
{{ if hugo.IsProduction }}
{{ $css = $css | resources.PostCSS | fingerprint | resources.PostProcess }}
{{ end }}
<link rel="stylesheet" type="text/css" media="screen" href="{{ $css.Permalink }}" />
Et votre fichier postcss.config.js
comme ceci
const purgecss = require('@fullhuman/postcss-purgecss')({
content: [ './hugo_stats.json' ],
whitelist: [
'img'
],
defaultExtractor: (content) => {
let els = JSON.parse(content).htmlElements;
return els.tags.concat(els.classes, els.ids);
}
});
module.exports = {
plugins: [
require('autoprefixer'),
...(process.env.HUGO_ENVIRONMENT === 'production' ? [ purgecss ] : [])
]
};
Erreur sur les images
Comme PurgeCSS
fonctionne sur un principe de liste blanche il est
possible que si hugo ne détecte pas bien un tag HTML dans votre code il
ne l’ajoute pas dans le fichier hugo_stats.json
et ainsi que celui-ci
soit supprimé par PurgeCSS
. C’est ce qui m’est arrivé avec la balise
HTML img
.
Si vous souhaitez ajouter explicitement un tag, une classe ou un ID à la liste de blanche de PurgeCSS il est possible de le faire comme ceci.
const purgecss = require('@fullhuman/postcss-purgecss')({
content: [ './hugo_stats.json' ],
whitelist: [
'img'
],
defaultExtractor: (content) => {
let els = JSON.parse(content).htmlElements;
return els.tags.concat(els.classes, els.ids);
}
});
...
Résultat et conclusion
Au final pour une page très simple j’avais 2 fichiers CSS pour un poids initial et total de 9.1Ko. Après purge (sans minification) je tombe à 6.1Ko. Enfin si j’active la minification j’arrive à 4.5Ko.
J’arrive donc à diviser la taille de mes ressources CSS par deux sur une page déjà très légère alors imaginer sur des pages plus complexe faisant intervenir de nombreux fichiers CSS.