Résoudre les problèmes de FOUC avec Craft CMS sur Firefox : le parcours d'un vrai utilisateur

Résoudre les problèmes de FOUC avec Craft CMS sur Firefox : le parcours d'un vrai utilisateur

Alex Rollin
Alex Rollin
January 2, 2026
Dernière mise à jour : February 21, 2026
January 2, 2026

Vous chargez votre site Craft CMS dans Firefox, et pendant une fraction de seconde, la page a l'air d'avoir oublié de s'habiller. Le texte s'étale sur l'écran en Times New Roman, les boîtes de mise en page s'empilent verticalement au lieu de rester dans leur grille, puis—clac—tout se replace. C'est le FOUC (Flash of Unstyled Content), et si vous le voyez seulement dans Firefox alors que Chrome et Safari se comportent parfaitement, vous faites face à l'un des problèmes spécifiques aux navigateurs les plus frustrants en développement web.

Ce guide vous accompagne pour comprendre pourquoi Firefox gère le chargement CSS différemment, comment les configurations Craft CMS (particulièrement celles utilisant Vite ou Webpack) peuvent déclencher ces flashs, et les étapes spécifiques pour diagnostiquer et corriger le problème. Nous couvrirons les coupables habituels, vous donnerons des exemples de code fonctionnels, et partagerons l'approche de débogage qui fonctionne vraiment.

Prérequis

Avant de plonger dans le dépannage, assurez-vous d'avoir :

  • Une installation Craft CMS 4 ou 5 où vous pouvez modifier les templates
  • Accès à la configuration de votre outil de build (Vite, Webpack, ou Laravel Mix si applicable)
  • Une familiarité avec les outils de développement Firefox (nous utiliserons les onglets Réseau et Console)
  • Une compréhension de base de comment le chargement CSS affecte le rendu de la page

Vous devriez aussi être capable de reproduire le problème de FOUC de manière constante. Si ça n'arrive qu'occasionnellement, notez si ça se produit au premier chargement, lors d'un rafraîchissement forcé, ou après avoir vidé le cache.

Étape 1 : Comprendre pourquoi Firefox se comporte différemment

Le moteur de rendu de Firefox commence à afficher le contenu plus tôt dans le processus de chargement que Chrome. Quand votre CSS n'est pas disponible au moment où Firefox décide de faire le rendu, vous obtenez un FOUC. Chrome tend à attendre un peu plus longtemps, ce qui masque les problèmes de synchronisation que Firefox expose.

L'avertissement dans la console que vous pourriez voir—« Layout was forced before the page was fully loaded. If stylesheets are not yet loaded this may cause a flash of unstyled content »—c'est Firefox qui vous dit exactement ce qui se passe. Votre HTML fait son rendu avant que le CSS soit prêt.

Trois patterns causent couramment ce problème dans les projets Craft CMS :

CSS chargé via JavaScript : Quand Vite ou Webpack importe le CSS dans votre bundle JavaScript, les styles sont injectés après l'exécution du script. La page fait son rendu, puis les styles s'appliquent, créant un flash visible.

Patterns de chargement CSS asynchrone : La technique rel="preload" avec échange onload pour le CSS non critique peut mal fonctionner dans Firefox, surtout si elle est utilisée pour votre feuille de style principale.

Mauvaise configuration de l'outil de build : Les configurations de développement qui injectent les styles via Hot Module Replacement peuvent parfois se retrouver dans les builds de production.

Étape 2 : Inspecter votre livraison CSS actuelle

Ouvrez votre site Craft dans Firefox avec les DevTools ouverts. Allez dans l'onglet Réseau, filtrez par « CSS », et rechargez la page avec le cache désactivé (maintenez Shift en cliquant sur recharger, ou cochez « Désactiver le cache » dans les paramètres des DevTools).

Cherchez ces signes avant-coureurs :

Aucune requête CSS avant le premier affichage : Si vos requêtes de feuilles de style commencent après les fichiers JavaScript, ou pire, n'apparaissent que comme partie des requêtes de bundles JS, Firefox affichera le contenu non stylisé en premier.

Seulement des liens preload, pas de liens stylesheet : Vérifiez votre source HTML. Si vous voyez ce pattern pour votre CSS principal :

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

Sans un <link rel="stylesheet"> de secours, Firefox pourrait ne pas appliquer les styles jusqu'à ce que le gestionnaire JavaScript onload se déclenche—ce qui arrive après le rendu initial.

CSS importé seulement dans JavaScript : Dans votre layout Twig, si vous voyez seulement des balises script et aucun lien de feuille de style, votre outil de build injecte probablement le CSS via JavaScript.

Étape 3 : Vérifier la structure de votre template Twig

Ouvrez votre template de layout principal (typiquement templates/_layout.twig ou similaire). Votre section <head> devrait contenir des liens explicites vers les feuilles de style pour le CSS critique.

Voici à quoi ressemble un layout Craft résistant au FOUC :

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ siteName }}</title>
    
    {# CSS critique - bloque le rendu jusqu'au chargement #}
    <link rel="stylesheet" href="{{ alias('@web') }}/dist/critical.css">
    
    {# Feuille de style principale - aussi bloquante pour la fiabilité Firefox #}
    <link rel="stylesheet" href="{{ alias('@web') }}/dist/main.css">
    
    {# Précharger les polices pour éviter les décalages de mise en page #}
    <link rel="preload" href="{{ alias('@web') }}/fonts/your-font.woff2" 
          as="font" type="font/woff2" crossorigin>
    
    {% block headStyles %}{% endblock %}
</head>
<body>
    {% block content %}{% endblock %}
    
    {# Scripts à la fin du body #}
    <script type="module" src="{{ alias('@web') }}/dist/app.js"></script>
</body>
</html>

La différence clé avec beaucoup de configurations modernes : le CSS se charge via des balises <link rel="stylesheet"> simples dans <head>, pas à travers des imports JavaScript.

Étape 4 : Corriger la configuration de l'outil de build

Si vous utilisez Vite avec Craft CMS, votre build de production doit extraire le CSS dans des fichiers séparés plutôt que de les bundler avec JavaScript.

Pour les utilisateurs de Vite, vérifiez votre vite.config.js :

import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    // S'assurer que le CSS est extrait dans des fichiers séparés
    cssCodeSplit: true,
    rollupOptions: {
      output: {
        // Noms de fichiers CSS prévisibles pour les templates Craft
        assetFileNames: (assetInfo) => {
          if (assetInfo.name.endsWith('.css')) {
            return 'css/[name].[hash].css'
          }
          return 'assets/[name].[hash][extname]'
        }
      }
    }
  },
  css: {
    // Empêcher le CSS d'être inliné dans le JS
    devSourcemap: true
  }
})

Ensuite dans vos templates Twig, liez le fichier CSS extrait directement plutôt que de compter sur le JavaScript de Vite pour l'injecter :

{% if craft.app.config.general.devMode %}
    {# Développement : Vite gère le CSS via HMR #}
    <script type="module" src="http://localhost:5173/@vite/client"></script>
    <script type="module" src="http://localhost:5173/src/main.js"></script>
{% else %}
    {# Production : Lier le CSS extrait directement #}
    <link rel="stylesheet" href="{{ alias('@web') }}/dist/css/main.css">
    <script type="module" src="{{ alias('@web') }}/dist/app.js"></script>
{% endif %}

Pour les utilisateurs de Webpack, assurez-vous que MiniCssExtractPlugin est configuré pour la production :

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader, // Extrait le CSS dans des fichiers
          'css-loader',
          'postcss-loader'
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/[name].[contenthash].css'
    })
  ]
}

Étape 5 : Gérer correctement les polices web

Les polices sont une cause secondaire courante de FOUC. Quand les polices personnalisées se chargent après le rendu initial, le texte se réorganise et la mise en page se décale.

Ajoutez font-display: swap à vos déclarations de polices :

@font-face {
  font-family: 'YourFont';
  src: url('/fonts/your-font.woff2') format('woff2');
  font-display: swap;
}

Préchargez les polices critiques dans votre layout Twig :

<link rel="preload" 
      href="{{ alias('@web') }}/fonts/your-font.woff2" 
      as="font" 
      type="font/woff2" 
      crossorigin>

L'attribut crossorigin est requis pour le préchargement des polices, même pour les polices de même origine.

Étape 6 : Tester vos modifications

Après avoir fait des modifications, testez systématiquement :

  • Rafraîchissement forcé dans Firefox avec les DevTools ouverts, cache désactivé
  • Vérifiez la cascade réseau : les requêtes CSS devraient commencer et se terminer avant ou en même temps que le chargement du document principal
  • Testez avec throttling réseau : dans les DevTools Firefox, réglez le Réseau sur « Good 3G » et rechargez—ça exagère les problèmes de synchronisation et rend le FOUC plus visible s'il est encore présent
  • Testez en mode incognito/privé : les extensions peuvent injecter du CSS ou retarder le chargement; la navigation privée élimine ces facteurs

En travaillant avec des équipes, on a appris que le test le plus fiable est d'ouvrir le site sur un profil Firefox vierge sans extensions et sans données en cache. Si le FOUC apparaît là, le problème est dans votre code, pas dans l'environnement de l'utilisateur.

Erreurs courantes à éviter

Erreur 1 : Utiliser du CSS async pour votre feuille de style principale

Le pattern preload/onload swap fonctionne bien pour le CSS véritablement non critique (comme les styles pour une modale qui apparaît sur interaction utilisateur), mais l'utiliser pour votre CSS de mise en page principal invite le FOUC de Firefox.

<!-- Ne faites pas ça pour les styles principaux -->
<link rel="preload" href="/dist/main.css" as="style" 
      onload="this.onload=null;this.rel='stylesheet'">

<!-- Faites ça à la place -->
<link rel="stylesheet" href="/dist/main.css">

Erreur 2 : Supposer que le comportement du serveur de dev correspond à la production

Le serveur de dev de Vite utilise le Hot Module Replacement, qui injecte le CSS via JavaScript. Ça fonctionne bien pendant le développement mais ne devrait pas être répliqué en production. Vérifiez toujours que votre build de production extrait le CSS dans des fichiers séparés.

Erreur 3 : Sur-optimiser le CSS critique

Les outils de CSS critique extraient les styles au-dessus de la ligne de flottaison pour l'inlining. Si l'extraction est incomplète ou si le seuil est réglé trop agressivement, les utilisateurs voient du contenu partiellement stylisé jusqu'à ce que le CSS complet se charge. On a trouvé que garder le CSS critique complet (couvrant toute la mise en page et typographie visibles, pas juste le strict minimum) prévient ce problème.

Erreur 4 : Oublier le registerCss() de Craft

Si vous utilisez des modules ou plugins Craft qui appellent registerCss() ou registerCssFile(), ces styles pourraient s'injecter après le rendu initial. Vérifiez vos modules pour vous assurer qu'ils n'ajoutent pas de styles critiques de cette façon.

Étapes de vérification

Une fois que vous avez implémenté les corrections, vérifiez avec cette liste :

  • Voir la source de la page : Confirmez que les balises <link rel="stylesheet"> apparaissent dans <head> avant tout script
  • Vérification de l'onglet Réseau : Les fichiers CSS devraient avoir « Initiator » montrant le document HTML, pas un fichier JavaScript
  • Plusieurs rechargements Firefox : Faites 5-10 rafraîchissements forcés; les problèmes de FOUC peuvent être intermittents
  • Test réseau ralenti : Le FOUC ne devrait pas apparaître même sur des connexions lentes
  • Vérification de la console : L'avertissement Firefox « Layout was forced » devrait être absent

Quand les corrections standard ne fonctionnent pas

Si vous avez fait tout ce qui précède et voyez encore occasionnellement du FOUC Firefox, vous êtes probablement en train de frapper une bizarrerie de synchronisation spécifique à Firefox. La communauté a documenté un contournement : ajouter un petit script inline à la fin de <head> :

<head>
    {# ... vos balises meta et feuilles de style ... #}
    
    <link rel="stylesheet" href="{{ alias('@web') }}/dist/main.css">
    
    {# Correctif de synchronisation FOUC Firefox - placer juste avant </head> #}
    <script>let FF_FOUC_FIX;</script>
</head>

Cette déclaration de script vide semble influencer suffisamment la synchronisation du rendu de Firefox pour prévenir le flash. Ce n'est pas un correctif documenté, mais des développeurs ont confirmé que ça fonctionne encore fin 2024. Notre expérience montre que ce hack devient inutile une fois que la livraison CSS est correctement structurée, mais c'est un outil de diagnostic utile—si ajouter ce script corrige votre FOUC, vous savez que la cause racine est la synchronisation des feuilles de style plutôt que quelque chose d'autre entièrement.

Résumé

Le FOUC Firefox dans les projets Craft CMS remonte presque toujours à la synchronisation du chargement CSS : soit les styles sont injectés via JavaScript au lieu d'être liés directement, soit les patterns CSS async ne jouent pas bien avec le pipeline de rendu de Firefox, soit les outils de build ne sont pas configurés pour extraire le CSS pour la production. La correction implique de s'assurer que les liens de feuilles de style bloquant le rendu sont dans votre <head>, de configurer Vite ou Webpack pour extraire le CSS correctement, et de tester avec le cache désactivé et le throttling réseau pour attraper tout problème restant.

On a aidé plusieurs équipes à résoudre exactement ces problèmes de style Firefox avec leurs builds Craft CMS. Si vous voyez encore du FOUC après avoir travaillé ce guide, ou si votre configuration implique des scénarios plus complexes comme plusieurs bundles CSS ou des couches de cache côté serveur, on serait contents de jeter un œil à votre configuration spécifique et vous aider à identifier ce qui cause le décalage de synchronisation.

Share this article