Le chargement parfait des styles CSS

J'essaye de charger les styles CSS de la façon qui me semble la plus optimisée possible avec Jekyll

Avec l'aide d'astuces Liquid, je demande à Jekyll d'includer uniquement les blocs de styles CSS utilisés dans la page et de les afficher directement dans la balise HEAD

Le problème

Je veux pouvoir afficher le site le plus rapidement possible au visiteur sans avoir de chargement de fichiers superflux. Sans avoir à demander au navigateur de parser des informations CSS qui ne servent à rien dans la page en cours (donc pas de fichier global qui contient l’ensemble des styles du site). Je cherche à donner au visiteur seulement ce dont il a besoin au moment où il en a besoin pour afficher la page en cours.

Il faut garder en tête que le parsage et la prise en compte des styles CSS demande des ressources et de la mémoire au navigateur. Plus on lui en envoie, et plus c’est long sur les devices lents et/ou anciens.

Pour plus d’accéssibilité, je veux que mon document HTML soit validé W3C. Les styles CSS doivent donc être placés dans une balise STYLE, dans la balise HEAD

Le tout doit être minifié et décommenté pour gagner quelques ko supperflus.

La solution doit être extensible. Je dois pouvoir avoir autant de templates, layout et feuilles de style que je veux.

J’utilise Jekyll, je dois pouvoir faire ça facilement avec des tags Liquid.

En bref, n’avoir qu’un fichier HTML à charger qui contient le contenu HTML ainsi que les styles necessaires placés dans la balise HEAD. De cette façon, le contenu CSS est gzippé avec le reste du fichier HTML par le serveur web. On se retrouve avec très peu de données à transférer.

Ma solution

Structure de fichiers

Pour que ce soit plus pratique pour moi, je créé un fichier common.html qui contient l’ensemble de page html (de <html> à </html>) dans laquelle viendra se glisser le contenu du layout. Et de la même façon, je créé un fichier common.css qui va contenir le CSS qui sera utilisé sur toutes les pages du site.

Si j’ai un fichier html que je risque d’inclure dans mon contenu un peu partout (dans un layout, ou bien simplement directement dans un post), alors je créé un fichier CSS du même nom. Exemple : video.html, video.css

Pour chacun de mes templates, je créé un fichier CSS du même nom (s’il y a besoin d’y associer un CSS indépendant). Exemple : post.css, posts.css

Ca donne ceci :

 _includes/
      common.html
      common.css
      post.css
      posts.css
      video.html
      video.css
 _layouts/
      default.html
      post.html
      posts.css

Au niveau des layouts

Dans mon layout, la première chose que je fais, c’est de capturer les styles CSS du layout en cours dans une variable generated_css :

<!-- _layouts/post.html (1/3) -->
{% capture generated_css %}
    {{ generated_css }}
    {% include posts.css %}
{% endcapture %}

(je concatène avec une éventuelle variable generated_css existante pour permettre d’ajouter tous les styles utilisés dans la page. Ici ça ne servira pas, mais comme ça, j’utilise la même formule partout)

Ensuite, je capture l’ensemble de mon contenu dans une variable generated_content :

<!-- _layouts/post.html (2/3) -->
{% capture generated_content %}
    <section class="post">
    [...]
    </section>
{% endcapture %}

… à la suite de quoi, j’include mon fichier common.html

<!-- _layouts/post.html (3/3) -->
{% include common.html %}

Mon fichier common.html va alors recevoir les deux variable generated_css et generated_content. Il n’aura qu’à les placer au bon endroit de la page :

<!-- _include_/common.html -->
<html>
    <head>
        <style>
            {{ generated_css }}
        </style>
    </head>
    <body>
        {{ generated_content }}
    </body>
</html>

Au niveau des posts et des pages

Au niveau des posts et pages, c’est un peu différent. Si je créé une variable generated_css directement dans le contenu, elle ne sera pas transmise au layout. Je dois donc mettre l’information dans la configuration. Par exemple, pour cette page, je charge le style CSS qui me permet d’afficher des blocs de code avec coloration syntaxique. J’ajoute donc la variable YML contains_code comme ceci :

    ‑‑‑
    # 2021-09-07-my-blog-post.md
    layout: post
    contains_code: true

    ‑‑‑

    Hello world :
    ```
    <b>Hello world</b> is bold
    ```

… et au niveau de mon fichier common.html, je peux alors indiquer que si contains_code est à true, alors je charge le style CSS pour la coloration syntaxique du code (le fichier _includes/code.css par exemple).

<!-- _include_/common.html -->

{% if page.contains_code == true %}
    {% capture generated_css %}
        {{ generated_css }}
        {% include code.css %}
    {% endcapture %}
{% endif %}

<html>
[...]
</html>

Encore plus extensible…

La solution ci-dessus fonctionne bien. Par contre, c’est assez difficilement extensible. Je suis obligé d’ajouter une condition dans common.html pour chaque feuille de style envisageable.

Pour avoir un résultat plus extensible, je peux ajouter un tag html directement dans le contenu de MarkDown de mon post qui ressemble à ceci :

<!--INCLUDE_CSS code.css END_INCLUDE_CSS-->

Et ensuite, dans mon fichier common.html, je peux lui demander de trouver l’ensemble de ces tags qui se trouve dans le contenu de ma page (dans la variable generated_content), d’en extraire le nom du fichier à inclure, de les lister, de retirer les doublons et d’ajouter leur contenu à la variable generated_css :

<!-- _include_/common.html -->


{% assign CSS_list = "" %}

{% assign CSS_blocs = generated_content | split: "<​!--INCLUDE_CSS" %}

{% for CSS_bloc in CSS_blocs %}
  {% assign CSS_bloc_name_array = CSS_bloc | split: "END_INCLUDE_CSS--​>" %}
  {% assign CSS_bloc_name_size = CSS_bloc_name | size %}
  {% assign CSS_bloc_name = CSS_bloc_name_array[0] | strip %}
  {% if CSS_bloc_name_size > 1%}
    {% assign CSS_list = CSS_list | append: "," | append: CSS_bloc_name %}
  {% endif %}
{% endfor %}

{% assign CSS_list = CSS_list | split: "," | uniq  %}

{% for CSS_inc in CSS_list  %}
    {% if CSS_inc !="" %}
        {% capture generated_css %}
            {{ generated_css }}
            {% include {{ CSS_inc }} %}
        {% endcapture %}
    {% endif %}
{% endfor %}

<html>
[...]
</html>

Ce coup-ci, on est pas mal. Ne reste plus qu’à minifier tout ça. Avec Jekyll, j’utilise le plugin jekyll-minifier avec l’option compress_css: true et remove_comments: true. De cette façon, je me retrouve avec une balise <style> bien remplie et bien minifiée. Et mes tags perso <!--INCLUDE_CSS code.css END_INCLUDE_CSS--> disparaissent du code en production.

Avantage collatéral

Je peux ajouter des balises Liquid dans mes feuilles de style. Pour par exemple définir une couleur dans mon fichier _config.yml et l’utiliser dans mon code CSS :

a{
    color:{{ site.color }};
}

Autres méthodes

Je pense qu’il est possible de faire ça avec SASS, mais je le maîtrise moins.

Quoi qu’il en soit, je suis plutôt content du résultat.

J’espère que ça vous aura aidé !

Le saviez-vous ? Je n'ai aucun moyen de savoir que vous avez lu cet article. Je respecte trop votre vie privée pour installer un tracker analytique ou un système de cookie. Du coup le seul moyen pour moi de savoir que quelqu'un lit ce que je raconte, c'est de lire vos commentaires.

Votre commentaire

A propos de vous...

Vous n'êtes quand même pas un vilain spammeur ?

Retourner en haut