Skip to main content

Chapitre 47 : Stratégie de chargement du CSS critique

Extraction et injection du CSS nécessaire au rendu initial (Above-the-fold)

Le concept de CSS Critique

1. Quoi

Le CSS Critique désigne l'ensemble minimal des règles CSS nécessaires pour afficher correctement la partie de la page visible immédiatement lors du chargement, sans que l'utilisateur n'ait besoin de scroller. Cette zone est appelée l'Above-the-fold (au-dessus de la ligne de flottaison).

L'objectif technique est de transformer le CSS, qui est par défaut une ressource bloquante pour le rendu (render-blocking), en une stratégie hybride :

  1. Injection inline du CSS critique dans le <head> du document HTML.
  2. Chargement asynchrone du reste du CSS (le CSS non-critique) pour ne pas retarder l'affichage initial.

2. Pourquoi

Dans un flux de navigation standard, le navigateur doit télécharger et analyser l'intégralité des fichiers CSS référencés par des balises <link> avant de pouvoir commencer à peindre (paint) le premier pixel sur l'écran.

Sur des sites complexes avec des fichiers CSS volumineux (plusieurs centaines de Ko), cela crée un délai perceptible appelé First Contentful Paint (FCP) élevé. En isolant le CSS critique, on obtient :

  • Amélioration du LCP (Largest Contentful Paint) : L'élément le plus large de la zone visible s'affiche quasi instantanément.
  • Réduction du temps de blocage : Le navigateur n'attend plus la fin du téléchargement d'un fichier externe pour afficher le haut de page.
  • Meilleure expérience utilisateur (UX) : La page semble charger instantanément, même si les styles du bas de page arrivent quelques millisecondes plus tard.

3. Comment

A. Syntaxe de base : L'injection Inline

La méthode consiste à placer les styles essentiels directement dans une balise <style> dans le <head>.

<head>
<style>
/* CSS Critique : Uniquement ce qui est visible au premier écran */
body { font-family: sans-serif; margin: 0; }
.header { height: 60px; background: #333; color: white; display: flex; align-items: center; }
.hero { padding: 40px; font-size: 2rem; text-align: center; }
</style>


<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
</head>

B. Cas concret : Implémentation robuste

Dans un projet professionnel, extraire le CSS critique à la main est impossible. On utilise des outils d'automatisation (comme Critical, Critters ou des plugins Webpack/Vite).

Voici le flux de travail recommandé pour une production scalable :

  1. Analyse : L'outil lance un navigateur sans tête (Headless Chrome), charge la page à différentes résolutions (Mobile, Desktop) et identifie les sélecteurs CSS utilisés pour les éléments visibles.
  2. Extraction : L'outil génère un fichier .css contenant uniquement ces règles.
  3. Injection : Lors du build ou via le serveur (SSR), ce contenu est injecté dans le HTML.
  4. Différé : Le fichier CSS complet est chargé via un pattern de "preload" pour éviter de bloquer le rendu.

<head>
<title>Ma Page Optimisée</title>


<style>
/* Généré automatiquement : contient header, nav, hero-section, typography-base */
:root{--primary:#007bff}.header{position:fixed;top:0;width:100%;z-index:100}.hero-title{font-size:3rem;margin-bottom:1rem}
</style>



<link rel="preload" href="/dist/main.css" as="style">


<script>
window.addEventListener('load', function() {
var link = document.querySelector('link[rel="preload"]');
if (link) {
link.rel = 'stylesheet';
}
});
</script>
</head>

C. Limitations et pièges

  • Maintenance : Si vous modifiez le design de votre header, le CSS critique doit être régénéré, sinon l'utilisateur verra un saut visuel.
  • Cache HTML : Le CSS inline ne peut pas être mis en cache indépendamment du HTML. Si votre CSS critique est trop volumineux (supérieur à 14 Ko), il peut augmenter la taille du premier paquet TCP, annulant le bénéfice de performance.
  • Complexité du Build : L'ajout d'une étape d'extraction augmente le temps de compilation.

4. Zone de Danger

L'erreur commune : Le FOUC (Flash of Unstyled Content) Le FOUC se produit lorsque le HTML est rendu avant que le CSS ne soit appliqué. Si vous chargez tout votre CSS de manière asynchrone sans CSS critique, l'utilisateur verra pendant une fraction de seconde une page blanche avec du texte brut et des images non positionnées, avant que le style ne "saute" brusquement en place.

La bonne pratique : Toujours garantir que les styles de structure (Layout), les couleurs de fond principales et la typographie de base sont présents dans le bloc <style> inline. Le CSS asynchrone ne doit servir qu'à styliser les éléments "Below-the-fold" (footer, modales, sections basses) ou les interactions complexes.

Flux de rendu standard (Bloquant)

Questions clés

1. Quelle est la différence entre le CSS critique et le CSS "minifié" ?

Découvrir la réponse

La minification réduit la taille du fichier en supprimant les espaces et commentaires, mais le navigateur doit toujours télécharger l'intégralité du fichier. Le CSS critique, lui, réduit la quantité de règles chargées initialement en ne gardant que celles nécessaires au premier écran.

2. Pourquoi limiter le CSS critique à environ 14 Ko ?

Découvrir la réponse

C'est une contrainte liée au protocole TCP (TCP Slow Start). Le premier round-trip (RTT) entre le serveur et le client transporte généralement environ 14 Ko de données. Si le HTML + le CSS critique tiennent dans ce premier paquet, le rendu peut commencer immédiatement sans attendre un deuxième aller-retour réseau.

3. Que se passe-t-il si le JavaScript chargé pour activer le CSS asynchrone échoue ?

Découvrir la réponse

C'est pourquoi il est crucial d'inclure une balise <noscript> contenant un <link rel="stylesheet"> classique. Ainsi, les utilisateurs sans JS ou avec un script bloqué auront toujours un style, même si le chargement sera bloquant.

4. Comment identifier les éléments "Above-the-fold" ?

Découvrir la réponse

Cela dépend de la résolution de l'écran. En général, on considère les zones de 1280x800 (Desktop) et 375x667 (Mobile). Les outils d'extraction simulent ces viewports pour déterminer quels éléments sont visibles.

Mise en pratique

Exercice 1 : Reproduction guidée

Objectif : Implémenter manuellement un pattern de chargement critique pour une page simple.

Énoncé : Vous avez un fichier style.css de 50 Ko. Vous remarquez que seul le header et le titre principal sont visibles au chargement.

  1. Créez une page HTML.
  2. Extrayez manuellement les styles du header et du titre dans une balise <style> dans le <head>.
  3. Chargez le fichier style.css de manière asynchrone en utilisant l'attribut onload sur la balise <link>.

Propriétés CSS à injecter en inline (dans la balise <style>) :

SélecteurPropriétéValeur
bodyfont-familyArial, sans-serif
bodymargin0
bodypadding0
headerbackground#2c3e50
headercolorwhite
headerpadding1rem
headertext-aligncenter
h1color#2c3e50
h1text-aligncenter
h1margin-top2rem
Découvrir la solution commentée
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Exercice CSS Critique</title>

<style>
/* CSS Critique : On définit le layout minimal pour éviter le FOUC */
body { font-family: Arial, sans-serif; margin: 0; padding: 0; }
header { background: #2c3e50; color: white; padding: 1rem; text-align: center; }
h1 { color: #2c3e50; text-align: center; margin-top: 2rem; }
</style>


<link rel="preload" href="style.css" as="style" onload="this.onload=null;this.rel='stylesheet'">


<noscript><link rel="stylesheet" href="style.css"></noscript>
</head>
<body>
<header>Mon Site Performance</header>
<h1>Bienvenue sur ma page optimisée</h1>
<div class="content">

<p>Le reste du contenu est chargé asynchronement...</p>
</div>
</body>
</html>

Exercice 2 : Adaptation

Objectif : Gérer le cas d'un site avec un "Dark Mode" utilisateur.

Énoncé : Le CSS critique est injecté en dur dans le HTML. Cependant, votre site propose un mode sombre basé sur une classe .dark-theme ajoutée à <html> via le localStorage. Comment adapter votre stratégie de CSS critique pour éviter un flash blanc avant que le JS ne détecte le thème ?

Mini-cours JavaScript — Les trois outils utilisés dans la solution

La solution fait appel à trois mécanismes JavaScript simples, expliqués ici pour les débutants.

1. localStorage — Mémoriser un choix dans le navigateur

localStorage est un espace de stockage dans le navigateur qui persiste même après fermeture de l'onglet.

localStorage.setItem('theme', 'dark');   // Enregistre la valeur
localStorage.getItem('theme'); // Lit la valeur → 'dark'
localStorage.getItem('inexistant'); // Clé inconnue → null

2. L'opérateur || — Valeur par défaut

const theme = localStorage.getItem('theme') || 'light';
// Si localStorage retourne null (première visite), on utilise 'light' par défaut

3. document.documentElement.classList.add() — Ajouter une classe sur <html>

document.documentElement cible la balise <html>. On y ajoute la classe du thème détecté avant que la page ne soit affichée, ce qui évite le flash :

document.documentElement.classList.add('dark-theme');
// Résultat HTML : <html class="dark-theme">

4. L'IIFE — Exécution immédiate et isolation

Une IIFE (Immediately Invoked Function Expression) est une fonction qui s'exécute immédiatement après sa définition. Elle isole les variables pour ne pas polluer l'espace global :

(function() {
// Tout ce code s'exécute immédiatement
// et les variables déclarées ici ne sont pas accessibles ailleurs
})();

HTML de départ :

<!DOCTYPE html>
<html lang="fr">
<head>
<!-- TODO : Ajouter le script de détection du thème ici (avant le CSS) -->

<style>
/* TODO : Définir les variables CSS pour les deux thèmes */
body { font-family: sans-serif; margin: 0; }
header { height: 60px; padding: 0 1rem; display: flex; align-items: center; }
</style>

<link rel="preload" href="main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="main.css"></noscript>
</head>
<body>
<header>Mon Application</header>
<main>
<h1>Contenu principal</h1>
<p>Voici le contenu de la page...</p>
</main>

<!-- Bouton pour tester le basculement de thème -->
<button onclick="
document.documentElement.classList.toggle('dark-theme');
localStorage.setItem('theme', document.documentElement.classList.contains('dark-theme') ? 'dark' : 'light');
">Basculer le mode sombre</button>
</body>
</html>

Ce que vous devez compléter :

  1. Ajouter les variables CSS --bg et --text dans :root et .dark-theme dans le bloc <style>.
  2. Écrire le script IIFE de détection du thème qui lit le localStorage et applique la bonne classe sur <html>.
Découvrir la solution commentée

L'astuce consiste à injecter un petit script de blocage (blocking script) juste avant le CSS critique pour définir les variables de couleur.

<head>
<script>
// Script synchrone et minuscule pour éviter le flash de couleur
(function() {
const theme = localStorage.getItem('theme') || 'light';
document.documentElement.classList.add(theme + '-theme');
})();
</script>

<style>
/* CSS Critique avec variables CSS */
:root { --bg: #ffffff; --text: #000000; }
.dark-theme { --bg: #1a1a1a; --text: #ffffff; }

body {
background-color: var(--bg);
color: var(--text);
font-family: sans-serif;
}
header { height: 60px; background: var(--bg); border-bottom: 1px solid var(--text); }
</style>

<link rel="preload" href="main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
</head>