Bonnes pratiques 22 octobre 2026 13 min de lecture

CSS et performance web : les nouveautés à adopter en 2026

Gabriel Janez

Gabriel Janez

Développeur Full-Stack PHP

Pendant des années, améliorer la performance d'un site voulait dire alléger le JavaScript, compresser les images et mettre en cache. Le CSS restait en arrière-plan. Ce n'est plus le cas : depuis deux ans, les navigateurs ont livré une série de propriétés qui réduisent le travail de rendu, stabilisent la mise en page et surtout remplacent des bibliothèques JavaScript entières.

Moins de JavaScript, c'est moins de code à télécharger, à analyser et à exécuter sur le thread principal. C'est donc un meilleur INP (réactivité), un meilleur LCP (affichage) et un CLS plus faible (stabilité). Voici les nouveautés qui comptent, avec leur support réel au 22 octobre 2026.

Méthode : les niveaux de support viennent du projet web-features (W3C WebDX), qui calcule le statut Baseline à partir des données de compatibilité de MDN. Baseline « nouvellement disponible » signifie que la fonctionnalité marche dans la dernière version de Chrome, Edge, Firefox et Safari. Baseline « largement disponible », qu'elle le fait depuis au moins 30 mois.

Vue d'ensemble

FonctionnalitéGain principalSupport
content-visibility: autoRendu initial, LCPBaseline depuis sept. 2025
Popover + commandforMoins de JS, INPBaseline depuis déc. 2025
@starting-style + allow-discreteAnimations sans JSBaseline depuis août 2024
field-sizing: contentMoins de JSBaseline depuis juin 2026
scrollbar-gutter: stableCLSBaseline depuis déc. 2024
Unités svh / dvh / lvhCLS sur mobileLargement disponible
overflow-anchorCLSBaseline depuis sept. 2026
View Transitions (même page)Performance perçueBaseline depuis oct. 2025
View Transitions (entre pages)Performance perçueChrome 126, Safari 18.2, pas Firefox
Anchor positioningMoins de JSChrome 125, Safari 26, Firefox 147 (partiel)
Animations liées au scrollThread principal libéréChrome 115, Safari 26, pas Firefox

1. content-visibility : ne rendre que ce qui est visible

Par défaut, le navigateur calcule le style, la mise en page et le dessin de toute la page, y compris ce qui est très loin sous la ligne de flottaison. content-visibility: auto lui permet de sauter ce travail pour les blocs hors écran, et de le faire seulement quand l'utilisateur s'en approche.

Prenons une longue liste de produits :

<section class="liste-produits">
  <article class="product-row">
    <img src="/img/produit-1.webp" alt="Chaise en chêne" width="320" height="240" loading="lazy">
    <h2>Chaise en chêne</h2>
    <p class="prix">149 €</p>
  </article>
  <!-- … 200 autres produits … -->
</section>

Une seule règle CSS suffit pour que seuls les produits proches de l'écran soient rendus :

.article-card,
.comment,
.product-row {
  content-visibility: auto;
  contain-intrinsic-size: auto 420px;
}

contain-intrinsic-size est indispensable. Sans lui, un bloc non rendu a une hauteur de 0 et la barre de défilement saute à mesure que les blocs apparaissent. Le mot-clé auto demande au navigateur de mémoriser la vraie taille une fois le bloc rendu ; 420px n'est que l'estimation de départ.

Dans la démo publiée par l'équipe Chrome sur web.dev, appliquer cette propriété à des sections de contenu a rendu le rendu initial 7 fois plus rapide. Le gain est surtout visible sur les longues pages (listes de produits, flux, documentation) et sur les mobiles d'entrée de gamme.

À ne pas faire : ne l'appliquez pas aux éléments visibles au chargement, et surtout pas à celui qui détermine le LCP (image principale, titre). Vous retarderiez son affichage au lieu de l'accélérer. Le contenu masqué reste dans le DOM : il est indexé, accessible aux lecteurs d'écran et trouvable avec Ctrl+F.

2. Remplacer du JavaScript par du CSS

C'est le gisement le plus important. Chaque bibliothèque de menu, d'infobulle ou d'animation au scroll ajoute du code à télécharger et des écouteurs d'événements qui s'exécutent sur le thread principal, celui qui doit aussi répondre aux clics.

Menus et modales : popover et commandfor

L'attribut popover gère l'affichage au-dessus de la page, la fermeture au clic extérieur et à la touche Échap, et le focus. Avec les invoker commands, plus besoin d'une seule ligne de JavaScript pour l'ouvrir :

<button commandfor="menu" command="toggle-popover">Menu</button>

<nav id="menu" popover>
  <a href="/services">Services</a>
  <a href="/contact">Contact</a>
</nav>

Animer une apparition : @starting-style

Animer un élément qui passe de display: none à visible demandait du JavaScript (ajouter une classe à la frame suivante). @starting-style définit l'état de départ, et transition-behavior: allow-discrete permet d'animer display lui-même. Appliqué au menu popover ci-dessus, il apparaît en fondu, toujours sans JavaScript :

[popover] {
  opacity: 0;
  translate: 0 -8px;
  transition: opacity .2s, translate .2s,
              overlay .2s allow-discrete,
              display .2s allow-discrete;
}

[popover]:popover-open {
  opacity: 1;
  translate: 0 0;
}

@starting-style {
  [popover]:popover-open {
    opacity: 0;
    translate: 0 -8px;
  }
}

Infobulles et menus déroulants : anchor positioning

Positionner une infobulle à côté de son déclencheur, et la retourner quand elle sort de l'écran, est le travail de bibliothèques comme Floating UI ou Popper. Le CSS sait désormais le faire seul. Le HTML reste minimal :

<p>
  Prix : 149 €
  <button type="button" class="trigger" aria-describedby="aide-tva">?</button>
  <span id="aide-tva" role="tooltip" class="tooltip">Prix TTC, TVA 20 % incluse.</span>
</p>

Le CSS relie l'infobulle au bouton, l'affiche au survol ou au focus clavier, et la place en dessous, ou au-dessus s'il n'y a plus de place :

.trigger {
  anchor-name: --aide;
}

.tooltip {
  display: none;
  position: absolute;
  position-anchor: --aide;
  position-area: bottom;
  position-try-fallbacks: flip-block;
}

.trigger:is(:hover, :focus-visible) + .tooltip {
  display: block;
}

Chrome (125), Safari (26) et Firefox (147) prennent en charge le cœur de la fonctionnalité. Certaines parties avancées ne sont pas encore alignées entre navigateurs, c'est pourquoi elle n'a pas encore le label Baseline et figure dans Interop 2026. Pour un usage simple, comme ci-dessus, elle est utilisable.

Formulaires : field-sizing

Un <textarea> qui s'agrandit avec son contenu nécessitait un script qui recalcule la hauteur à chaque frappe, exactement le genre de travail qui dégrade l'INP. C'est maintenant une seule ligne, Baseline depuis juin 2026 :

<label for="message">Votre message</label>
<textarea id="message" name="message"></textarea>
textarea {
  field-sizing: content;
  min-block-size: 3lh;
  max-block-size: 12lh;
}

Animations au scroll : sans écouteur JavaScript

Barres de progression de lecture, apparitions au défilement : ces effets reposaient sur des écouteurs scroll ou sur IntersectionObserver. Les scroll-driven animations lient directement une animation CSS à la position de défilement. Si elles n'animent que transform et opacity, le navigateur peut les exécuter hors du thread principal.

Côté HTML, une barre vide et des sections marquées :

<div class="progression-lecture" aria-hidden="true"></div>

<article>
  <section class="reveal">
    <h2>Première partie</h2>
    <p>…</p>
  </section>
  <section class="reveal">
    <h2>Deuxième partie</h2>
    <p>…</p>
  </section>
</article>

Tout le reste est en CSS. La barre se remplit avec le défilement de la page, et chaque section apparaît en entrant à l'écran :

@supports (animation-timeline: scroll()) {
  .progression-lecture {
    position: fixed;
    inset: 0 0 auto;
    block-size: 4px;
    background: #a855f7;
    transform-origin: 0 50%;
    animation: progression linear both;
    animation-timeline: scroll(root);
  }

  .reveal {
    animation: apparition linear both;
    animation-timeline: view();
    animation-range: entry 0% cover 30%;
  }
}

@keyframes progression { from { transform: scaleX(0); } }
@keyframes apparition  { from { opacity: 0; translate: 0 2rem; } }

Support : Chrome depuis la version 115 et Safari depuis la 26, mais pas encore Firefox en version stable. Le bloc @supports est donc indispensable : sans lui, les éléments .reveal resteraient invisibles dans Firefox.

3. Stabilité visuelle : réduire le CLS

Mobile : svh plutôt que 100vh

Sur mobile, 100vh correspond à la hauteur de l'écran sans la barre d'adresse. Quand celle-ci se rétracte ou réapparaît au défilement, une section en 100vh déborde, et tout ce qui est en dessous se décale. Les nouvelles unités règlent le problème :

  • svh (small) : la plus petite hauteur, barres visibles. Stable, idéale pour un bloc d'en-tête.
  • lvh (large) : la plus grande hauteur, barres masquées.
  • dvh (dynamic) : suit la hauteur en temps réel. À éviter pour les gros blocs, car elle provoque justement des recalculs de mise en page.
<section class="hero">
  <h1>Développeur PHP freelance</h1>
  <a class="btn" href="/contact">Demander un devis</a>
</section>
.hero {
  min-block-size: 100svh;
}

Desktop : scrollbar-gutter

Sur desktop, quand une page devient assez longue pour afficher une barre de défilement, tout le contenu se décale de 15 pixels environ. Même chose à l'ouverture d'une modale qui bloque le scroll. scrollbar-gutter: stable réserve l'espace dès le départ :

html {
  scrollbar-gutter: stable;
}

Contenu chargé au-dessus : overflow-anchor

Quand une publicité ou une image se charge au-dessus de l'endroit où lit l'utilisateur, le texte saute. Le scroll anchoring garde la position de lecture stable. Chrome et Firefox le faisaient déjà ; avec Safari 27, la propriété overflow-anchor est devenue Baseline en septembre 2026. Le comportement est automatique : la propriété sert surtout à le désactiver (overflow-anchor: none) sur un conteneur où il gênerait, comme un chat qui défile vers le bas.

<div class="chat" role="log" aria-live="polite">
  <p class="message">Bonjour, une question sur votre devis ?</p>
  <!-- les nouveaux messages sont ajoutés en bas -->
</div>
.chat {
  max-block-size: 60svh;
  overflow-y: auto;
  overflow-anchor: none;
}

Polices : un fallback aux mêmes dimensions

Pendant le chargement d'une police web, le texte s'affiche dans une police système puis change. Si les deux n'ont pas les mêmes dimensions, les lignes se décalent. Les descripteurs @font-face permettent d'ajuster la police de secours pour qu'elle occupe le même espace :

@font-face {
  font-family: "Inter Fallback";
  src: local("Arial");
  size-adjust: 107%;      /* valeurs d'exemple, */
  ascent-override: 90%;   /* à calculer pour votre police */
  descent-override: 22%;
  line-gap-override: 0%;
}

body {
  font-family: Inter, "Inter Fallback", sans-serif;
}

Côté HTML, préchargez la police web pour réduire la durée de la substitution. L'attribut crossorigin est obligatoire pour une police, même servie depuis votre propre domaine, sinon le navigateur la télécharge deux fois :

<link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin>

Des outils comme Fontaine calculent ces valeurs automatiquement. size-adjust fonctionne partout. En revanche, Safari ne prend pas en charge ascent-override, descent-override et line-gap-override : il les ignore simplement, et l'ajustement y est donc partiel.

4. Performance perçue : les View Transitions

Une transition fluide entre deux pages donne l'impression d'un site plus rapide, même à temps de chargement égal. Pour un site classique en PHP, sans framework JavaScript, deux lignes suffisent, à placer dans le CSS des deux pages :

@view-transition {
  navigation: auto;
}

@media (prefers-reduced-motion: reduce) {
  @view-transition {
    navigation: none;
  }
}

Les transitions entre pages fonctionnent dans Chrome (126+) et Safari (18.2+), pas encore dans Firefox, qui fait simplement une navigation normale. Comme elles n'ajoutent aucun JavaScript, c'est une amélioration sans risque.

Pour aller plus loin, donnez le même view-transition-name à un élément présent sur les deux pages. Ici, l'image d'une carte sur la liste et l'image d'en-tête de l'article : le navigateur l'anime de sa position dans la liste à sa place dans l'article.

<!-- Page /articles -->
<a href="/css-performance-web">
  <img src="/img/css-performance.webp" alt="" style="view-transition-name: couverture-42">
</a>

<!-- Page /css-performance-web -->
<img src="/img/css-performance.webp" alt="" style="view-transition-name: couverture-42">

Chaque nom doit être unique dans une page : utilisez l'identifiant de l'article, comme ici couverture-42.

Les transitions à l'intérieur d'une même page (avec document.startViewTransition()) sont Baseline depuis octobre 2025.

Mobile ou desktop : où les gains se voient le plus

Ces techniques profitent aux deux, mais pas au même degré :

  • Sur mobile, le processeur est bien moins puissant qu'un ordinateur récent (Lighthouse simule d'ailleurs un mobile en ralentissant le processeur 4 fois), et l'INP y est généralement moins bon. content-visibility et le remplacement du JavaScript y ont le plus d'effet. Les unités svh y règlent un défaut propre au mobile.
  • Sur desktop, les gains de calcul sont moins visibles, mais la stabilité compte toujours : scrollbar-gutter y corrige un décalage que le mobile ne connaît pas, puisque les barres de défilement s'y superposent au contenu.
  • Partout, méfiez-vous des effets coûteux à dessiner, comme backdrop-filter: blur() sur de grandes surfaces ou sur des éléments qui bougent : ils sont nettement plus lourds sur un GPU mobile.

À surveiller : encore réservé à Chromium

  • interpolate-size: allow-keywords : animer une hauteur vers auto (accordéons) sans JavaScript. Chrome 129+ uniquement, mais sans risque : les autres navigateurs ouvrent l'accordéon sans animation.
  • appearance: base-select : un <select> natif entièrement stylable, qui rendra inutiles les bibliothèques de listes déroulantes. Chrome 135+ et Safari 27, pas encore Firefox.
  • Container scroll-state queries : styler un en-tête quand il devient « collé » (position: sticky) sans IntersectionObserver. Chrome 133+ uniquement.

Le <select> personnalisable montre bien la direction prise : le HTML reste un vrai <select>, accessible et compatible avec les formulaires, mais il accepte désormais du contenu riche dans ses options :

<select name="pays">
  <button><selectedcontent></selectedcontent></button>
  <option value="fr"><img src="/flags/fr.svg" alt=""> France</option>
  <option value="be"><img src="/flags/be.svg" alt=""> Belgique</option>
  <option value="ch"><img src="/flags/ch.svg" alt=""> Suisse</option>
</select>
select,
::picker(select) {
  appearance: base-select;
}

Dans les navigateurs qui ne le prennent pas en charge, c'est un <select> classique qui s'affiche : le contenu riche est ignoré, le texte des options reste.

Mesurer avant de crier victoire

Chaque changement doit se vérifier, idéalement sur un vrai téléphone de milieu de gamme :

  • Chrome DevTools, onglet Performance : comparez la durée de la phase « Rendering » avant et après content-visibility, avec le processeur ralenti 4 fois (CPU throttling).
  • Onglet Rendering, option Layout Shift Regions : les zones qui se décalent apparaissent en bleu.
  • Données réelles : Lighthouse mesure un chargement en laboratoire, mais l'INP et le CLS se jugent sur les données de vos visiteurs, dans la Search Console (rapport Core Web Vitals) ou CrUX.

Conclusion

Le CSS est devenu un vrai levier de performance. Les plus gros gains viennent rarement d'une astuce isolée, mais du JavaScript qu'on peut supprimer : un menu en popover, une infobulle en anchor positioning, un champ en field-sizing, ce sont autant de scripts en moins sur le thread principal. Commencez par ce qui est Baseline (content-visibility, popover, @starting-style, svh, scrollbar-gutter) et ajoutez le reste en amélioration progressive, derrière @supports.

Sources :

Tags

CLS Core Web Vitals CSS INP Mobile Performance

Partager

Articles similaires

PHP 7 min

Faille PHP critique : mettre à jour PHP sur un VPS Ubuntu

php -v affiche 8.4.11 alors que la 8.4.27 est sortie : votre serveur est-il vulnérable ? Correctifs rétroportés, mise à jour ciblée, migration vers Ubuntu 26.04 et PHP 8.5, et les pièges rencontrés : retour d'expérience sur mon VPS.

Besoin d'aide sur votre projet ?

Discutons de vos besoins et trouvons ensemble la meilleure solution technique.

Me contacter