Accueil / Articles / Prévenir le décalage cumulatif de mise en page avec des arrière-plans vidéo dans Next.js

Prévenir le décalage cumulatif de mise en page avec des arrière-plans vidéo dans Next.js

Apprenez des techniques pratiques de CSS et d’agencement pour empêcher les vidéos en arrière-plan dans Next.js de provoquer un décalage cumulatif d’agencement sur différents appareils et à différentes vitesses de connexion.

1289 mots

Il existe un type spécifique de bug frontend qui reste invisible tant que vous développez dans des conditions idéales, mais dès que votre connexion réseau ralentit, la vidéo en arrière-plan que vous avez ajoutée fait sauter votre page comme dans une scène chaotique d’une sitcom.

Félicitations : vous venez de rencontrer le Cumulative Layout Shift.

Les arrière-plans vidéo échappent facilement aux contrôles de qualité car ils se trouvent en arrière-plan, fonctionnent correctement sur une connexion rapide, et les ingénieurs ont tendance à considérer la vidéo comme une couche purement visuelle plutôt que comme un élément qui participe à l’agencement de la page.

Cependant, le navigateur ne partage pas nos hypothèses. Lorsque la taille d’un élément est inconnue au moment de l’affichage initial, le navigateur doit deviner. Or, ce sont précisément ces suppositions que l’on ne souhaite pas voir influencer les décisions d’agencement.

Avec ce contexte établi, examinons maintenant les détails techniques.

La règle fondamentale : réserver de l’espace avant le chargement de la vidéo

Cumulative Layout Shift mesure l’ampleur du déplacement inattendu du contenu visible. La façon la plus simple d’éviter cela consiste à attribuer aux éléments multimédias des dimensions prévisibles et connues avant que leurs ressources ne soient entièrement téléchargées. En définissant explicitement la largeur et la hauteur, ou en utilisant la propriété CSS aspect-ratio, on permet au navigateur d’allouer cet espace à l’avance lors du calcul de la mise en page.

En termes simples : réservez de l’espace pour un élément avant même qu’il ne soit affiché.

Pour une section principale avec une vidéo en arrière-plan, un bon point de départ est d’utiliser un conteneur aux dimensions fixes, plutôt que de laisser la balise <video> elle-même déterminer la taille de cette zone.

export function VideoHero() {
  return (
    <section className="relative min-h-[70svh] overflow-hidden">
      <video
        className="absolute inset-0 h-full w-full object-cover"
        autoPlay
        muted
        loop
        playsInline
        preload="metadata"
        aria-hidden="true"
      >
        <source src="/hero-video.mp4" type="video/mp4" />
      </video>

      <div className="relative z-10 mx-auto max-w-6xl px-6 py-24">
        <h1 className="text-5xl font-bold text-white">
          Build products people remember.
        </h1>
      </div>
    </section>
  );
}

La partie cruciale ici n’est absolument pas le marquage de la vidéo.

C’est cette seule ligne :

min-height: 70svh;

Grâce à cela, la section principale présente déjà une empreinte visuelle identifiable avant même que la vidéo ne commence à jouer.

La vidéo elle-même est positionnée de manière absolue à l’intérieur de ce conteneur, ce qui signifie qu’elle ne peut pas déplacer soudainement le contenu en dessous d’elle une fois que ses ressources ont fini de se charger.

Cette différence modifie complètement le niveau de stabilité perçu par la page.

Ne laissez pas la vidéo définir votre mise en page

Une configuration typique et problématique ressemble à ceci :

<video
  src="/hero-video.mp4"
  autoPlay
  muted
  loop
/>

Puis quelqu’un ajoute :

video {
  width: 100%;
}

Et plus tard, on se demande pourquoi la page se comporte de manière incohérente en fonction de la qualité de la connexion ou de la taille de l’écran.

Le problème est que le navigateur doit connaître à l’avance les dimensions de la vidéo. Si une vidéo n’est qu’un contenu décoratif d’arrière-plan, il y a rarement de bon motif pour qu’elle participe au flux normal du document.

Préférez plutôt confier la responsabilité de l’agencement à le conteneur d’enveloppement, c’est-à-dire le div qui entoure l’élément vidéo.

<div style="height: 100px; width: 100%">
  <video
    src="/hero-video.mp4"
    autoPlay
    muted
    loop
  />
</div>

Avec cette structure en place, la vidéo peut se comporter comme elle le souhaite à l’intérieur, mais le div qui l’entoure maintient tout cela en son sein.

Utilisez object-fit: cover pour les vidéos en arrière-plan

Lorsque la vidéo est positionnée absolument, object-fit: cover devient véritablement utile (un cas rare où une propriété CSS fonctionne exactement comme prévu).

.heroVideo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

Cela permet à la vidéo de remplir complètement l’aire réservée sans jamais modifier les dimensions du conteneur lui-même.

Il existe un petit compromis à noter : la valeur cover coupera des parties de la vidéo chaque fois que le rapport d’aspect de l’écran ne correspond pas à celui du fichier source. C’est un prix raisonnable à payer.

L’erreur réelle consiste à donner la priorité à une conservation parfaite au niveau des pixels de la vidéo source, alors que ce dont a réellement besoin le design, c’est d’un layout stable et prévisible.

Utiliser une affiche comme premier état visuel

Une technique particulièrement efficace consiste à fournir une image d’affiche bien choisie pour l’élément vidéo.

<video
  className="absolute inset-0 h-full w-full object-cover"
  autoPlay
  muted
  loop
  playsInline
  preload="metadata"
  poster="/images/hero-poster.webp"
  aria-hidden="true"
>
  <source src="/videos/hero.mp4" type="video/mp4" />
</video>

L’affiche offre aux visiteurs quelque chose de concret à regarder pendant que la vidéo elle-même est encore en téléchargement. Choisissez cette image avec soin, car elle constitue en fait votre design de secours.

Mais surtout, cela signifie que votre layout ne dépend plus du fait que la ressource vidéo soit immédiatement disponible.

Considérez l’affiche comme l’état de base fiable, avec la vidéo elle-même superposée en tant qu’amélioration progressive, tout comme un chargeur squelette ou un indicateur de chargement fonctionnerait dans d’autres contextes.

Si la vidéo met plusieurs secondes à se charger via une connexion mobile lente, la page reste visible et complète, plutôt que brisée ou partiellement chargée.

Faites attention aux pièges de 100vh

Les vidéos en plein écran présentent un autre piège subtil.

Auparavant, les développeurs écrivaient généralement :

height: 100vh;

Les navigateurs mobiles compliquent les choses, car la hauteur de la zone visible change à mesure que le chrome du navigateur (barre d’adresses, barres d’outils) apparaît et disparaît, ce qui fait que 100vh ne se comporte pas comme on pourrait s’y attendre.

Pour des layouts qui doivent s’adapter correctement à différents appareils, il est généralement préférable d’utiliser de nouvelles unités de vue, telles que :

min-height: 100svh;

ou, en fonction de la partie de l’écran que vous souhaitez que le contenu principal occupe :

min-height: 80svh;

En bref : préférez 100dvh ou 100svh à l’ancienne version 100vh.

Évitez de servir des vidéos en taille desktop aux utilisateurs mobiles

Même une page avec un CLS parfait peut sembler extrêmement lente si la vidéo d’arrière-plan est trop volumineuse.

C’est là que les aspects liés aux performances deviennent plus complexes.

Un clip cinématographique volumineux de 12 MB peut se jouer parfaitement sur une connexion desktop rapide, mais sur un appareil mobile via un réseau instable, ce même fichier devient un véritable fardeau.

Pour la plupart des pages d’accueil, il est judicieux de fournir des fichiers vidéo distincts pour les écrans desktop et mobiles.

Dans certains cas, il est préférable d’omettre complètement la vidéo pour certains visiteurs.

Respecter la préférence d’un utilisateur pour des animations réduites en est un bon exemple :

const prefersReducedMotion =
  window.matchMedia("(prefers-reduced-motion: reduce)").matches;

Dans une application React réelle, cette vérification doit se trouver dans un composant client, et vous devez la gérer avec prudence afin qu’elle ne perturbe pas l’HTML rendu sur le serveur.

La règle de base est simple : des considérations liées aux performances et à l’accessibilité doivent déterminer si la vidéo est affichée ou non, et non seulement sa vitesse de téléchargement.

Ne laissez jamais la vidéo dicter le layout

C’est le principe directeur à garder en tête à tout moment.

La structure de votre page doit fonctionner correctement avec cette séquence :

Hero container
    ↓
Poster
    ↓
Video enhancement

et jamais en fonction de celle-ci :

Video starts loading
    ↓
Browser discovers dimensions
    ↓
Hero changes height
    ↓
Everything below moves
    ↓
Lighthouse gets angry

Le navigateur doit déjà comprendre votre layout avant que l’élément multimédia plus lourd n’apparaisse.

C’est la véritable solution aux problèmes CLS causés par les vidéos.

Pour le vérifier en pratique, chargez votre site via une connexion réseau ralentie et contrôlée pour observer son comportement.

Lectures complémentaires

  • Propriétés personnalisées CSS en temps réel : Thématisation, ponts JavaScript et @property — Découvrez en quoi les propriétés personnalisées CSS diffèrent des variables Sass et apprenez à les utiliser pour la thématisation, les effets gérés par JavaScript, le type fluide, les variantes de composants et les animations typées.
  • Queries de conteneur vs Queries médias : Décisions pour les pages et les composants — Apprenez quand recourir à une query média et quand une query de conteneur est plus adaptée, grâce à des exemples de cartes, de tableaux de bord et d’unités qui permettent aux composants réutilisables de s’adapter.