Ingénierie
Un site de studio en moins d’une seconde : notre budget de performance
Comment nous livrons des sites de studio qui affichent leur premier contenu en moins d’une seconde sur un téléphone milieu de gamme en 4G — le budget que nous nous imposons, ce que nous supprimons, et les petites décisions d’architecture qui comptent le plus pour les Core Web Vitals.
Dans cet article
Il existe un type bien particulier de site de studio — celui où chaque défilement déclenche un scintillement en parallaxe, un changement de police, une bande démo en lecture automatique qui charge pendant trois secondes et un curseur qui sort les crocs au survol — qui met huit secondes à se charger sur un téléphone milieu de gamme et semble cassé sur toute connexion autre que la fibre. Nous n’en livrons pas. Le cahier des charges que nous nous imposons est brutal et simple : un premier affichage de contenu en moins d’une seconde, sur un Pixel 6a, via une connexion 4G bridée.
Tenir ce cahier des charges ne relève pas de l’optimisation héroïque. Cela tient à un petit nombre de choix d’architecture qui se cumulent, et à un nombre bien plus grand de choses que nous ne faisons pas. Cet article présente le budget auquel nous nous tenons, les décisions qui le sous-tendent et les mesures terrain qui prouvent qu’il a été respecté. Si vous livrez un site de studio, un site d’agence ou tout support en forme de portfolio, c’est un objectif concret que vous pouvez reprendre presque tel quel.
Le budget, en chiffres
Chaque site de studio que nous livrons vise ces chiffres sur un Pixel 6a, avec le bridage Fast 3G de Chrome DevTools. En conditions réelles, une connexion 4G est plus rapide ; nous utilisons Fast 3G comme borne basse, dans le pire des cas. Nous mesurons avec Lighthouse et, en production, avec des métriques d’utilisateurs réels via Vercel Analytics.
- Largest Contentful Paint (LCP) : moins de 1,2 s en laboratoire, moins de 1,8 s au p75 sur le terrain. Le seuil « bon » de Google est de 2,5 s.
- First Contentful Paint (FCP) : moins de 1,0 s en laboratoire.
- Cumulative Layout Shift (CLS) : moins de 0,05. Le seuil « bon » de Google est de 0,1 ; nous nous imposons la moitié.
- Interaction to Next Paint (INP) : moins de 100 ms. Le seuil « bon » de Google est de 200 ms.
- Poids total transféré au premier chargement : moins de 150 Ko compressés, HTML, CSS, polices, JS et image principale compris.
- Bundle JavaScript au premier chargement : moins de 80 Ko compressés.
Si une page rate l’un de ces objectifs, nous le traitons comme un bug P1. Pas comme « un point à revoir plus tard ». Un P1, que nous corrigeons avant la mise en ligne du site.
Pourquoi autant de rigueur ?
Deux raisons. D’abord, les Core Web Vitals sont un facteur de classement pour Google. Pas un simple critère de départage — un facteur de classement. Les sites qui respectent les seuils sont éligibles au bonus « expérience de page » ; les autres sont pénalisés à la marge. Pour un studio qui se dispute des groupes de mots-clés étroits, la marge compte.
Ensuite, la performance perçue est un signal de marque. Un site qui se charge instantanément inspire la compétence. Un site qui se fige le temps qu’un pixel de suivi se charge inspire l’inverse. Pour un studio dont tout le discours est « nous livrons un travail réfléchi et moderne », le site doit en faire la démonstration dès le premier contact. Le site est le brief.
Décision 1 : tout rendre en statique
La décision la plus déterminante pour la performance, c’est la stratégie de rendu. Nous utilisons Next.js avec l’App Router, et chaque page de nos sites de studio est rendue en statique au moment du build. Aucune requête en base à l’exécution. Aucun travail serveur à chaque requête, hormis la fonction edge qui sert le HTML. Le site entier n’est qu’un ensemble de fichiers sur un CDN.
Ce que cela vous apporte, concrètement : le TTFB sur l’edge est celui de votre CDN — généralement 30 à 80 ms, partout sur la planète. Le premier octet de HTML arrive avant même que le navigateur ait terminé sa négociation TLS avec une alternative rendue côté serveur. Tout ce qui suit — l’affichage, l’interactivité, la page suivante — démarre d’autant plus tôt.
La contrepartie : chaque modification de contenu exige un déploiement. Pour un site de studio mis à jour chaque mois, un déploiement est un non-événement — il prend 90 secondes sur Vercel. Pour une publication mise à jour toutes les heures, le calcul est différent, et l’ISR (Incremental Static Regeneration) est le bon outil. Mais les sites de studio ne sont pas mis à jour toutes les heures.
Décision 2 : une police web, deux graisses
Les polices personnalisées sont la plus grosse dépense maîtrisable du premier affichage. Un sous-ensemble de police web pour une seule graisse pèse généralement 25 à 40 Ko compressés. Un site qui charge trois graisses de deux familles embarque 150 Ko de polices avant que le premier octet de contenu ne s’affiche. Nous livrons une seule famille en deux graisses, servie par la distribution auto-hébergée et hachée de next/font — aucune requête tierce vers Google Fonts, aucune résolution DNS vers un CDN de polices.
Nous en acceptons aussi la conséquence : pas d’italique, pas de graisse fine, pas d’extra-gras. Chaque distinction typographique doit passer par la taille, la couleur, l’interlettrage ou le contraste entre les deux graisses dont nous disposons. C’est une contrainte créative qui se rentabilise d’elle-même. Les sites qui ont besoin de huit graisses masquent souvent une hiérarchie faible sous la variété typographique ; ceux qui fonctionnent avec deux graisses doivent construire leur hiérarchie par la structure.
Décision 3 : le budget de l’image principale — et comment le dépenser
Sur un site de studio, le LCP est presque toujours l’image principale. Le budget LCP est donc le budget de l’image principale. Nous lui fixons un plafond strict : 80 Ko compressés sur ordinateur, 40 Ko sur mobile. Au-delà, c’est un défi créatif à résoudre par le choix ou le traitement de l’image, pas une excuse pour dépasser le budget.
Quatre pratiques qui nous permettent de tenir ce budget :
- Utiliser l’AVIF quand il est pris en charge, avec un repli en WebP. L’AVIF est généralement 30 à 40 % plus léger que le WebP à qualité équivalente.
- Servir des tailles adaptatives via le composant next/image. Les téléphones ne téléchargent jamais l’image principale destinée aux ordinateurs ; les ordinateurs ne téléchargent jamais la version Retina 2x, sauf si l’écran est réellement Retina.
- Définir explicitement la largeur et la hauteur de chaque image. C’est ce qui évite les décalages de mise en page — le navigateur réserve le bon nombre de pixels avant l’arrivée de l’image.
- Précharger l’image principale. <link rel="preload" as="image" href="…"/> dans le head. Cela coûte une ligne et fait gagner 200 à 400 ms sur le LCP, car le navigateur lance la requête de l’image avant d’analyser le reste de la page.
Décision 4 : le JavaScript en dernier recours
Chaque kilo-octet de JavaScript doit être téléchargé, analysé et exécuté sur le thread principal avant que la page ne devienne interactive. Nous traitons le budget JS comme une ressource rare et nous tenons à deux règles :
- Des Server Components par défaut. Ne marquer un composant « use client » que s’il a réellement besoin d’interactivité. Le comportement par défaut des React Server Components est le bon pour les sites riches en contenu.
- Aucun script tiers au premier chargement. Pas d’outil d’analytics qui embarque 80 Ko de code tiers. Pas de gestionnaire de balises. La mesure d’audience se charge en différé, une fois la page interactive — nous utilisons Vercel Analytics, qui pèse environ 1 Ko.
Le budget que nous imposons à nos sites de studio est de moins de 80 Ko de JS compressé au premier chargement — dont l’essentiel est React lui-même. La couche interactive (menu de navigation, sélecteur de langue, formulaire de contact) ne pèse que quelques kilo-octets.
Décision 5 : une architecture CSS sans décalages de mise en page
Le Cumulative Layout Shift est l’indicateur Core Web Vitals le plus facile à rater par accident, et le plus facile à corriger volontairement. Les quatre habitudes qui maintiennent le CLS proche de zéro :
- Réserver l’espace pour tout ce qui arrive tard. Images, polices, iframes, publicités — tout reçoit un conteneur aspect-ratio aux dimensions explicites.
- Éviter le CSS qui dépend du chargement de la police. Utiliser des polices système de repli pendant la fenêtre de substitution, avec font-display: swap. Choisir une police de repli dont les métriques correspondent à la police web, pour que la substitution soit invisible.
- Ne pas injecter de contenu au-dessus de la ligne de flottaison après le chargement. Les bannières, avis de cookies et barres d’annonce qui apparaissent après le premier affichage sont des décalages de mise en page en puissance. Si vous en avez besoin, rendez-les dans le HTML initial et faites-les apparaître par une animation.
- Tester sur des réseaux lents. Les problèmes de CLS invisibles sur la fibre apparaissent en 3G, parce que la ressource qui arrive tard arrive encore plus tard.
Décision 6 : le budget d’animation
La sobriété dans le mouvement est une optimisation de performance en soi. Chaque animation qui s’exécute sur le thread principal risque de dégrader l’INP — une seule animation saccadée de 250 ms au clic fait sortir tout le site de la catégorie « bon » pour l’INP. Nos règles :
- N’animer que transform et opacity. Les deux sont composités par le GPU et ne déclenchent pas de recalcul de mise en page.
- Les animations d’apparition sont en CSS, pas en JavaScript. Nous utilisons un IntersectionObserver pour ajouter une className ; l’animation elle-même est une transition.
- Pas d’animations liées au défilement. Elles semblent élégantes, mais dégradent terriblement le défilement sur les appareils milieu de gamme.
- Pas de vidéo en lecture automatique en tête de page. Si une vidéo est nécessaire, chargez-la en différé sous la ligne de flottaison ou derrière une interaction.
Décision 7 : là où accessibilité et performance se rejoignent
La plupart des gains d’accessibilité sont aussi des gains de performance. Le HTML sémantique est plus léger qu’une soupe de div. Les contours de focus natifs s’affichent plus vite qu’une gestion du focus sur mesure en JS. Les liens d’évitement en en-tête rendent inutiles les correctifs de pièges au clavier qui embarquent du JavaScript. Nous concevons et développons d’abord pour l’accessibilité ; la performance suit gratuitement.
Le seul point de tension entre performance et accessibilité, c’est l’animation. Certains utilisateurs ont besoin de mouvement ; d’autres en ont la nausée. Nous respectons prefers-reduced-motion et livrons chaque animation d’apparition avec une alternative en fondu — pas en translation.
Mesurer en production
Les métriques de laboratoire de Lighthouse sont nécessaires, mais pas suffisantes. La référence absolue, c’est le suivi des utilisateurs réels (RUM) — ce que vivent de vrais visiteurs sur de vrais appareils. Nous utilisons Vercel Analytics pour les Web Vitals, car il est inclus gratuitement avec la plateforme et remonte des métriques terrain segmentées par route, appareil et zone géographique.
Ce que nous regardons chaque semaine :
- Le LCP au p75 par route. Si le p75 d’une route dépasse 1,8 s, nous enquêtons.
- Les régressions d’INP. L’INP est la métrique la plus sournoise — elle peut se dégrader en silence à mesure que le JavaScript grossit.
- Le CLS par route. Les régressions de CLS remontent généralement à un seul élément chargé tardivement.
- Le JS du premier chargement par route, suivi via le bundle analyzer en CI. Nous faisons échouer les builds qui dépassent le budget.
Ce que nous ne faisons pas
Tout aussi important — ce que nous laissons de côté délibérément, même si c’est la norme dans le secteur :
- Pas de routage côté client façon SPA sur un petit site. Récupérer le document complet est plus rapide que le delta de bundle sur une connexion 4G. Nous laissons le navigateur naviguer.
- Pas de service worker. La complexité n’en vaut pas la peine sur un site de moins de 10 pages.
- Pas de service d’hébergement d’images qui injecte un SDK de 30 Ko. Nous servons les images depuis le même domaine que le site, optimisées au moment du build.
- Pas de bibliothèque « intelligente » de chargement différé. L’attribut natif loading="lazy" fait le travail pour 0 Ko.
- Pas de préchargement de chaque lien au survol. Next.js précharge par défaut en production ; nous laissons ce réglage activé et laissons le framework décider.
Si vous cadrez un site et souhaitez un audit de performance de l’existant, ou un site construit dès le départ selon ce budget : hello@neptay.com.