Galeries scroll-snap : contenu horizontal fluide sans JavaScript

Cet article vous a aidé ?

Vous avez livré une galerie horizontale « simple », et maintenant le support dit qu’elle « colle », « saute », « mange le défilement » ou « plante sur iPad ». Produit veut du fluide. Accessibilité veut la prise en charge clavier. Marketing veut un rendu type carrousel, mais « sans JS, s’il vous plaît ».

Bonne nouvelle : le scroll snapping CSS peut prendre en charge la plupart de ces besoins — si vous le concevez comme vous gérez des systèmes en production : prévisible, observable et résilient face aux mauvaises entrées (images de 14 Mo, débordements imbriqués, etc.). Mauvaise nouvelle : il vous permettra aussi de construire une galerie qui marche sur votre portable et ruine la journée des autres.

Ce qu’est réellement le scroll snap (et ce qu’il n’est pas)

Le scroll snapping n’est pas un « composant carrousel ». C’est une fonctionnalité du navigateur qui privilégie l’arrêt d’un conteneur de défilement à des points de snap définis. Vous fournissez des indices physiques ; l’agent utilisateur décide de l’arrivée. Cette distinction compte, car vous ne pouvez pas le commander comme du JS. Vous ne pouvez définir que des contraintes et laisser le navigateur négocier avec les dispositifs d’entrée, les paramètres d’accessibilité et l’intention de l’utilisateur.

Concrètement :

  • scroll-snap-type se place sur le conteneur défilant. Il déclare « snap sur l’axe x » et à quel degré de fermeté.
  • scroll-snap-align se place sur les enfants. Il déclare où chaque élément veut s’aligner par rapport au snapport du conteneur.
  • scroll-padding et scroll-margin sont les ajustements « oui, on a un en-tête fixe ».
  • scroll-behavior: smooth affecte le défilement programmatique (et certains comportements UA), pas la physique du toucher brut.

Ligne de base opinionnée : considérez le scroll-snap comme une amélioration progressive. La galerie doit rester utilisable en défilement horizontal simple même si le snapping est désactivé ou ignoré.

Deux choses que le scroll snap n’est pas :

  • Ce n’est pas déterministe. Si vous testez au trackpad, vous verrez un réglage de snap différent d’une roulette de souris, et encore différent au toucher. Ce n’est pas un bug ; c’est le flux d’entrée.
  • Ce n’est pas un substitut aux contrôles de navigation. Certains utilisateurs voudront des contrôles explicites « suivant/précédent ». « Pas de JS » ne signifie pas « pas de contrôles » ; cela signifie que vous utiliserez des ancres, le focus et une mise en page raisonnable.

Une idée paraphrasée de la culture d’ingénierie qui s’applique ici (et qui est la raison pour laquelle on mesure) : « L’espoir n’est pas une stratégie. » — idée souvent attribuée aux responsables de fiabilité/opérations. Si votre galerie « semble généralement correcte », vous n’avez pas encore testé les modes de défaillance.

Choix de conception qui font la différence entre premium et cassé

Mandatory vs proximity : choisissez vos batailles

mandatory impose le snap. C’est excellent pour les expériences « une carte à la fois ». C’est aussi le mode qui donne l’impression d’être piégé si vos points de snap sont trop denses ou vos cartes trop étroites. proximity est plus indulgent : il ne snappe que quand l’arrêt est suffisamment proche.

Si vos utilisateurs sont susceptibles de défiler rapidement dans un flux, préférez proximity. Si la galerie est un « stepper » intentionnel (photos produit, onboarding), mandatory est raisonnable.

Alignement du snap : start vs center vs end

scroll-snap-align: start est le plus prévisible car il correspond à la façon dont on lit les mises en page gauche-droite : le contenu commence à un bord constant. L’alignement center semble « soigné » mais est plus sensible aux arrondis, au padding du conteneur et aux différentes largeurs de viewport.

  • Utilisez start lorsque la carte contient du texte ou toute interface alignée à gauche.
  • Utilisez center lorsque la carte est principalement une image et que vous visez un rendu « coverflow-ish » (sans l’énergie 2007).
  • Évitez end sauf si vous avez une exigence spécifique d’alignement sur le bord droit.

scroll-padding et scroll-margin : la taxe des en-têtes fixes

Si votre mise en page comprend un en-tête fixe ou un overlay, le snapping peut placer le contenu en dessous. Utilisez scroll-padding sur le conteneur pour décaler les positions de snap par rapport aux bords. Utilisez scroll-margin sur les enfants quand seuls certains éléments nécessitent un décalage différent.

Pattern typique :

  • Le conteneur a scroll-padding-inline: 16px pour empêcher la première/dernière carte de coller au bord du viewport.
  • Le conteneur a scroll-padding-top si c’est du snapping vertical (ce qui n’est pas notre focus, mais même principe).

Espacement : gap vs margin, et pourquoi ça compte

Les points de snap sont calculés à partir des boîtes de mise en page. Si vous mélangez gap, marges et pseudo-éléments, vous pouvez finir avec des points de snap qui ne correspondent pas à ce que voit le designer. Restez ennuyeux :

  • Utilisez le gap du conteneur pour l’espacement entre éléments.
  • Évitez les marges par item sauf si vous avez besoin d’asymétrie.
  • Si vous avez besoin d’espace de tête/queue, utilisez le padding du conteneur plus scroll-padding.

Défilement imbriqué : c’est faisable, mais ça coûte

Le bug le plus courant « pourquoi ça semble cassé » est les conteneurs de défilement imbriqués : une galerie défilante horizontalement dans une page qui défile verticalement, plus une carte avec son propre overflow. Les trackpads et appareils tactiles doivent alors deviner où appliquer l’intention de défilement.

Utilisez :

  • overscroll-behavior: contain pour arrêter le chaînage de défilement quand c’est nuisible.
  • touch-action: pan-x sur le scroller horizontal pour réduire la confusion verticale.

Comportement de la barre de défilement : une mise en page stable vaut mieux que joli

Certaines plateformes superposent les barres de défilement ; d’autres réservent de l’espace. Quand une barre apparaît/disparaît, le contenu peut bouger, et les positions de snap peuvent se déplacer. C’est comme ça que vous obtenez des rapports « il a snapé sur la mauvaise diapositive » que vous ne pouvez pas reproduire.

Si possible, utilisez scrollbar-gutter: stable sur le conteneur de défilement pour éviter les déplacements de mise en page quand les barres apparaissent. Ce n’est pas universel, mais là où c’est supporté c’est un gain à faible effort.

Ne pas sur-ajuster à un seul appareil

Une roulette de souris envoie des deltas discrets. Un trackpad envoie des deltas continus et haute-résolution. Le tactile envoie de la vélocité avec décélération. Le scroll snapping se place en aval de tout cela. Tester uniquement avec une souris est la raison pour laquelle vous livrez une galerie qui vous semble correcte et hostile pour les autres.

Accessibilité et dispositifs d’entrée : tactile, trackpad, clavier

Support clavier sans JS : le focus est votre ami

Sans JavaScript, vous n’allez pas « écouter les flèches » et mettre à jour la position de défilement. Mais vous pouvez toujours fournir une expérience clavier raisonnable :

  • Rendez les éléments focalisables (tabindex="0") ou incluez du contenu focalisable à l’intérieur (liens/boutons).
  • Assurez-vous que les éléments focalisés sont visibles. Les navigateurs font généralement défiler les éléments focalisés dans la vue — souvent en respectant le snap.
  • Utilisez :focus-visible pour afficher un anneau de focus clair.

Si vous voulez des contrôles suivant/précédent explicites sans JS, vous pouvez le faire avec des liens d’ancrage vers des IDs sur les slides. C’est rétro, mais ça fonctionne et c’est accessible.

cr0x@server:~$ cat anchor-nav.html
<style>
  .gallery { overflow-x: auto; scroll-snap-type: x mandatory; display: grid; grid-auto-flow: column; grid-auto-columns: 85%; gap: 16px; padding: 16px; }
  .slide { scroll-snap-align: start; border: 1px solid #e6e6e6; border-radius: 12px; min-height: 200px; }
  .nav a { margin-right: 10px; }
  .gallery { scroll-behavior: smooth; }
  @media (prefers-reduced-motion: reduce) { .gallery { scroll-behavior: auto; } }
</style>

<div class="nav" aria-label="Gallery navigation">
  <a href="#s1">1</a>
  <a href="#s2">2</a>
  <a href="#s3">3</a>
</div>

<div class="gallery">
  <section id="s1" class="slide" tabindex="-1">Slide 1</section>
  <section id="s2" class="slide" tabindex="-1">Slide 2</section>
  <section id="s3" class="slide" tabindex="-1">Slide 3</section>
</div>

tabindex="-1" sur les slides permet de recevoir le focus quand on navigue par ancre sans les ajouter à l’ordre de tabulation. Cela conserve un ordre de tabulation cohérent sur la page.

Réduction du mouvement : ce n’est pas optionnel

Si vous ajoutez scroll-behavior: smooth, vous devez respecter prefers-reduced-motion. Le défilement fluide peut rendre certaines personnes nauséeuses, et il peut compliquer le débogage parce que chaque interaction devient animée. Activez le défilement fluide seulement si la préférence de mouvement le permet.

Lecteurs d’écran et sémantique

Les galeries scroll-snap restent des conteneurs de défilement. N’essayez pas de vous faire passer pour un « widget carrousel » à moins d’implémenter la sémantique interactive complète (ce qui implique généralement du JS). Restez sur un HTML propre :

  • Utilisez <section>, <article>, <figure> avec <figcaption> et des titres significatifs.
  • Étiquetez la région avec aria-label si elle n’est pas décrite par un texte adjacent.
  • Ne piégez pas le focus à l’intérieur de la galerie.

Touch-action : un outil tranchant

touch-action: pan-x peut améliorer l’intention horizontale sur les appareils tactiles, mais soyez prudent : si votre galerie est dans une page qui défile verticalement, vous voulez toujours permettre aux utilisateurs de défiler verticalement quand leur doigt n’est pas parfaitement horizontal. Testez. Si la galerie est haute et dense en contenu, vous préfèrerez peut-être laisser touch-action tranquille et compter sur un bon espacement et overscroll-behavior.

Performance : le vrai goulot d’étranglement n’est rarement « scroll snap »

Quand quelqu’un dit « le scroll snap est saccadé », il veut généralement dire « le défilement est saccadé quand le snap est activé ». C’est une différence importante. Le snapping peut exposer des problèmes de performance déjà présents : images surdimensionnées, thrash de layout, peinture coûteuse et compositing imbriqué.

Ce qui rend le snap désagréable

  • Décalages de mise en page pendant le défilement : images sans dimensions, police qui swappe, contenu dynamique chargé dans les cartes.
  • Peinture lourde : grosses ombres portées, filtres, backdrop-filter et grandes couches translucides.
  • Contention du thread principal : effets liés au scroll, sélecteurs CSS coûteux, trop d’éléments en position sticky.
  • Pression mémoire : de nombreuses images haute résolution décodées en même temps ; le navigateur commence à évincer des surfaces.

Stabilisez la mise en page d’abord : verrouillez les ratios d’image

Si la hauteur d’une carte change pendant le défilement, la géométrie du conteneur de défilement change et les points de snap peuvent sembler « bouger ». La solution est ennuyeuse mais efficace : définissez les attributs width/height des images (ou aspect-ratio en CSS) pour que le navigateur réserve l’espace avant le décodage.

Containment et content-visibility : utilisez, mais vérifiez

content-visibility: auto et contain peuvent améliorer les performances pour de grandes pages en sautant le rendu hors-écran. Mais dans les galeries scroll-snap, la région « hors-écran » est souvent à une carte seulement — et le snap peut forcer une révélation quasi-instantanée. Une utilisation excessive peut rendre l’atterrissage du snap semblable à un flash vide.

Utilisez-les lorsque vous avez beaucoup de slides lourdes. Puis validez sur appareils bas de gamme et Safari. Si vous voyez du blanchiment, réduisez l’agressivité ou préchargez une diapositive en avance.

Compositing : ne créez pas accidentellement 40 couches

Parsemer will-change: transform au hasard est l’équivalent frontend d’activer chaque couche de cache parce que « les caches sont rapides ». C’est ainsi que vous brûlez de la mémoire et obtenez des glitches bizarres.

Petite blague #2 : will-change revient à écrire « URGENT » sur tous les e-mails — à force, plus rien ne l’est.

Scroll snapping CSS et défilement fluide

scroll-behavior: smooth peut masquer des problèmes en donnant l’impression d’un mouvement « conçu », mais il peut aussi amplifier la sensation de latence car les animations vont saccader sous charge. N’utilisez pas le scroll-behavior comme pansement de performance. Corrigez la cause racine : layout et peinture.

Faits et courte histoire utiles à connaître

  1. Les idées de « points de snap » existaient dans les toolkits UI bien avant le CSS — pensez aux vues paginées des frameworks mobiles natifs.
  2. CSS Scroll Snap a commencé comme un effort du W3C pour formaliser le comportement de pagination pour les UIs tactiles, surtout avec l’explosion du browsing mobile.
  3. Les noms de propriétés ont évolué ; d’anciennes spécifications utilisaient d’autres noms, ce qui explique pourquoi vous verrez des snippets obsolètes en ligne.
  4. scroll-snap-stop existe parce que les utilisateurs se plaignaient de passer des éléments quand on flickait avec de la vélocité ; c’est un bouton de réglage pour « doit s’arrêter ici ».
  5. Le défilement à inertie n’est pas une fonctionnalité CSS — c’est un comportement de plateforme. Les algorithmes de snap doivent coexister avec la physique OS.
  6. Les moteurs de navigateur traitent le défilement comme un pipeline prioritaire ; les implémentations modernes essaient de garder le défilement hors du thread principal quand c’est possible.
  7. Le scroll snapping interagit avec des fonctionnalités d’accessibilité comme la réduction du mouvement ; les utilisateurs peuvent outrepasser votre intention, et c’est souhaitable.
  8. Les mises en page RTL compliquent le snapping horizontal parce que « start » et « end » s’inversent ; bien tester le RTL même si votre produit est majoritairement en anglais.
  9. Le rendu des barres de défilement varie selon l’OS et les paramètres ; les points de snap qui supposent une gouttière fixe peuvent dériver quand les barres ne sont pas overlay.

Trois micro-histoires d’entreprise des tranchées

Incident : la mauvaise hypothèse (« les points de snap sont juste les bords des cartes »)

Dans une entreprise de taille moyenne, une équipe produit a déployé une galerie scroll-snap pour une page de tarification. Ça rendait bien dans une capture d’écran Chrome sur desktop. Sur iOS Safari, des utilisateurs ont signalé que la galerie « refusait de se stabiliser » et parfois snapait sur ce qui ressemblait à une moitié de carte.

La mauvaise hypothèse était subtile : l’ingénieur pensait que les positions de snap s’aligneraient sur le bord visuel gauche de la carte. Mais le design utilisait des marges négatives sur les cartes pour créer un effet de « débordement », plus un pseudo-élément pour un fondu en dégradé. La boîte de layout n’était pas la même que le bord visuel.

Le support a reproduit le problème rapidement sur iPhone. L’ingénierie ne reproduisait pas sur MacBook avec trackpad. Ce décalage a retardé la correction parce que tout le monde a débattu pour savoir si c’était « réel ». C’était réel. C’était dépendant du dispositif d’entrée et de la boîte de layout.

La correction a été ennuyeuse : retirer les marges négatives, déplacer le débord décoratif dans du padding et des couches de fond à l’intérieur de la carte, et utiliser le gap du conteneur. Les positions de snap sont devenues stables parce qu’elles correspondaient aux boîtes utilisées par le navigateur pour calculer les points de snap.

Conclusion : quand vous construisez du scroll snap, vos boîtes de layout sont l’API. Si vos visuels ne correspondent pas aux boîtes, le navigateur snappera fidèlement sur les boîtes, pas sur vos intentions.

Optimisation qui a mal tourné (« on va lazy-render tout »)

Une autre équipe avait un scroller « vignettes feature » sur la homepage. Les performances allaient bien sur laptops, mais les appareils Android bégayaient fortement. Un ingénieur a ajouté content-visibility: auto sur chaque carte et a chargé les images de façon très paresseuse pour réduire le travail initial.

Les métriques de rendu initial se sont améliorées. Tout le monde a célébré. Puis les rapports de bug sont arrivés : les utilisateurs balayaient la galerie et voyaient des tuiles blanches pendant un instant, parfois assez longtemps pour paraître cassé. Le snapping a empiré le phénomène parce qu’il forçait le viewport à « atterrir » sur une tuile qui n’était pas encore rendue.

Le problème racine n’était pas que le rendu paresseux soit mauvais. C’est que le scroll-snap fait de l’élément « suivant » une cible garantie à court terme. Si vous sautez le rendu de façon trop agressive, vous créez un blanc visible exactement au moment de l’interaction, que les utilisateurs interprètent comme de la latence.

Le plan de récupération a été de conserver content-visibility, mais de le limiter : rendre la tuile courante plus une en avant/derrière en s’assurant que ces éléments sont « suffisamment visibles » (par ex. avec un seuil moins agressif ou en n’appliquant pas la propriété aux voisins immédiats). Aussi, pré-déclarez les tailles d’images et utilisez des sources responsives pour réduire les pics de décodage.

Leçon : optimisez pour l’interaction, pas pour les captures Lighthouse. Une galerie est une surface d’interaction. Traitez-la comme un chemin chaud, parce que c’en est un.

Pratique ennuyeuse mais correcte qui a sauvé la mise (« feature flag + rollback rapide »)

Un détaillant a remplacé un carrousel JS par du CSS scroll snap pour réduire la taille du bundle. Le changement était derrière un feature flag. Pas de fanfare. Juste un déploiement contrôlé avec un interrupteur d’arrêt.

Pendant la montée en charge, le service client a signalé que certains utilisateurs sous Safari ancien voyaient un comportement de type « caoutchouc » : la galerie sur-défilait et rebondissait, puis snapait sur le mauvais élément. Ce n’était pas universel, et la repro nécessitait des réglages OS spécifiques.

Au lieu de passer un week-end à débattre de savoir si Safari avait « tort », le SRE de garde a fait ce que doit faire un on-call : réduire le rayon d’impact. Ils ont désactivé le flag pour les user agents affectés pendant que l’équipe investiguait. Pas de drame. Pas de patch d’urgence. Pas de déploiement nocturne.

L’ingénierie a ensuite construit une petite mitigation basée sur l’UA : pour les versions problématiques de Safari, ils sont passés de mandatory à proximity et ont supprimé le défilement fluide. Ils ont aussi ajouté overscroll-behavior et simplifié les débordements imbriqués. Le taux de bugs est retombé au bruit de fond.

La pratique qui a sauvé la mise n’était pas du CSS astucieux. C’était de la discipline opérationnelle : montée en charge progressive, rapports d’erreur mesurables et chemin de rollback sans héroïsme.

Playbook de diagnostic rapide

Vous avez une galerie scroll-snap. Les utilisateurs disent qu’elle est saccadée, qu’elle saute sur de mauvais éléments ou qu’elle ne snappe pas. Vous avez besoin d’un chemin rapide vers le goulot sans transformer le débogage en mode de vie.

Première étape : confirmez que le conteneur et les points de snap sont réels

  1. Vérifiez que le conteneur de défilement est bien l’élément que vous pensez (overflow-x: auto sur le bon nœud).
  2. Vérifiez que les enfants sont des participants directs avec scroll-snap-align (non appliqué à un wrapper interne auquel vous n’accédez pas réellement).
  3. Vérifiez l’absence de conteneurs overflow imbriqués à l’intérieur des slides qui pourraient voler le défilement ou provoquer du chaînage de scroll.

Deuxième étape : isolez les changements de layout

  1. Désactivez le chargement d’images (ou remplacez par des placeholders de taille fixe) et voyez si le problème disparaît.
  2. Vérifiez si des polices se swapent après le premier paint.
  3. Cherchez des injections de contenu dynamiques (publicités, personnalisation, modules « recommandés ») qui changent la taille des cartes.

Troisième étape : profilez la performance du scroll comme un adulte

  1. Enregistrez un profil de performance pendant le défilement et le settle du snap.
  2. Cherchez de longues tâches sur le thread principal pendant le scroll.
  3. Cherchez des peintures/compositings lourds (grosses ombres, filtres, éléments en position fixe).

Quatrième étape : testez across input methods et réglages

  1. Trackpad vs roulette souris vs tactile (appareil réel si possible).
  2. Réduction du mouvement activée.
  3. RTL si votre produit le supporte (ou le supportera).

Règle de décision : si le snap est incorrect, corrigez la géométrie et le layout du snap. Si le snap est correct mais l’interaction est désagréable, corrigez la performance et la gestion d’entrée (overscroll/touch-action), ou relaxez mandatory en faveur de proximity.

Tâches pratiques : commandes, sorties et décisions

Voici le type de vérifications que j’attends dans un canal d’incident réel : rapides, déterministes et liées à une décision. Les commandes sont exécutables sur une machine de développement Linux typique. Quand la vérification est « frontend », on utilise quand même des outils système parce que le debugging en production est transversal.

Tâche 1 : Confirmer que le CSS de la galerie est effectivement déployé

cr0x@server:~$ curl -sS -D- https://example.test/gallery | head
HTTP/2 200
content-type: text/html; charset=utf-8
cache-control: public, max-age=60
etag: "a1b2c3"
...

Ce que signifie la sortie : Vous obtenez un 200 avec du HTML. La connectivité de base est bonne.

Décision : Si vous voyez une boucle de redirection ou un 500, arrêtez de blâmer le CSS et réparez la livraison d’abord.

Tâche 2 : Vérifier que le CSS contient les règles scroll-snap

cr0x@server:~$ curl -sS https://example.test/assets/app.css | grep -n "scroll-snap" | head
1842:.gallery{scroll-snap-type:x mandatory;scroll-padding-inline:16px}
1843:.card{scroll-snap-align:start}

Ce que signifie la sortie : Le bundle CSS inclut les propriétés. Si grep ne renvoie rien, votre pipeline de build a probablement tree-shaké les styles ou vous pointez vers le mauvais asset.

Décision : Des règles manquantes signifient un problème de déploiement/build, pas un bug navigateur. Corrigez le bundling ou les sélecteurs.

Tâche 3 : Vérifier si la compression fonctionne (CSS/HTML volumineux retardent l’interaction)

cr0x@server:~$ curl -sS -I https://example.test/assets/app.css | egrep -i "content-encoding|content-length|cache-control"
cache-control: public, max-age=31536000, immutable
content-encoding: br
content-length: 41231

Ce que signifie la sortie : Brotli est activé, la taille est gérable, le cache est solide.

Décision : S’il n’y a pas de compression et que le content-length est énorme, corrigez ça avant d’optimiser le snap.

Tâche 4 : Mesurer la taille des images (images surdimensionnées = coupable n°1 du jank)

cr0x@server:~$ curl -sS -I https://example.test/media/slide-1.jpg | egrep -i "content-type|content-length|cache-control"
content-type: image/jpeg
content-length: 8421932
cache-control: public, max-age=31536000

Ce que signifie la sortie : Environ 8 Mo pour une seule image. Sur mobile, c’est problématique même avec un CSS parfait.

Décision : Ajoutez des images responsives, des formats modernes et déclarez les dimensions. Réduisez les octets avant de débattre de l’alignement du snap.

Tâche 5 : Confirmer que le serveur supporte les requêtes range (utile pour le media)

cr0x@server:~$ curl -sS -I https://example.test/media/slide-1.jpg | egrep -i "accept-ranges"
accept-ranges: bytes

Ce que signifie la sortie : Les requêtes range sont activées.

Décision : Si absent, la livraison des gros médias peut être moins efficace. Vérifiez la config CDN/origin.

Tâche 6 : Identifier les longues tâches pendant le scroll sur une machine de test (pression CPU système)

cr0x@server:~$ top -b -n 1 | head -n 12
top - 10:21:14 up 12 days,  4:02,  1 user,  load average: 2.11, 1.88, 1.74
Tasks: 238 total,   1 running, 237 sleeping,   0 stopped,   0 zombie
%Cpu(s): 18.2 us,  3.3 sy,  0.0 ni, 77.8 id,  0.0 wa,  0.0 hi,  0.7 si,  0.0 st
MiB Mem :  15948.5 total,   2142.9 free,   6120.3 used,   7685.3 buff/cache
MiB Swap:   2048.0 total,   2048.0 free.   9182.2 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
 4121 cr0x      20   0 3241560 482912 156324 S  48.0   3.0  10:12.33 chrome

Ce que signifie la sortie : Chrome utilise beaucoup de CPU durant votre test d’interaction.

Décision : Si le CPU monte en flèche pendant le scroll, attendez-vous à du jank. Passez au profiling navigateur et réduisez le travail de paint/layout.

Tâche 7 : Vérifier si le système swappe (le swap rend tout spectral)

cr0x@server:~$ free -h
               total        used        free      shared  buff/cache   available
Mem:            15Gi       5.9Gi       2.1Gi       268Mi       7.5Gi       8.9Gi
Swap:          2.0Gi          0B       2.0Gi

Ce que signifie la sortie : Le swap n’est pas utilisé. Bon état de base pour des tests de perf fiables.

Décision : Si le swap est fortement utilisé, votre test de « jank scroll snap » est invalide. Réparez l’état de la machine d’abord.

Tâche 8 : Détecter les signaux de layout shift dans les logs de terrain (corrélation CLS)

cr0x@server:~$ journalctl -u webapp -n 30 | grep -i "CLS" | tail
Dec 29 10:19:12 web01 webapp[2381]: rum metric CLS=0.21 route=/pricing device=mobile
Dec 29 10:19:48 web01 webapp[2381]: rum metric CLS=0.19 route=/pricing device=mobile

Ce que signifie la sortie : Le CLS terrain est élevé sur la page qui héberge la galerie.

Décision : Priorisez la stabilité de la mise en page (dimensions d’images, chargement des polices) avant d’ajuster le comportement de snap. Le snap ne corrigera pas une géométrie instable.

Tâche 9 : Vérifier que les fichiers de polices ne sont pas énormes ou lents (les swaps peuvent déplacer les largeurs de carte)

cr0x@server:~$ curl -sS -I https://example.test/assets/fonts/Inter-var.woff2 | egrep -i "content-length|cache-control|content-type"
content-type: font/woff2
content-length: 986432
cache-control: public, max-age=31536000, immutable

Ce que signifie la sortie : Police d’environ 1 Mo. Pas forcément rédhibitoire, mais suspecte pour des swaps tardifs.

Décision : Envisagez le sous-ensemble ou assurez-vous que la stratégie font-display n’induit pas des changements visibles de layout dans les cartes.

Tâche 10 : Vérifier le cache HTTP (éviter le re-téléchargement des assets de galerie)

cr0x@server:~$ curl -sS -I https://example.test/assets/app.css | egrep -i "etag|last-modified|cache-control"
cache-control: public, max-age=31536000, immutable
etag: "d34db33f"

Ce que signifie la sortie : Cache fort avec assets immuables.

Décision : Si le cache est faible, les utilisateurs re-téléchargent et les interactions commencent tard. Corrigez le caching avant de discuter des micro-détails CSS.

Tâche 11 : Confirmer que votre page n’a pas désactivé par accident le défilement overflow

cr0x@server:~$ rg -n "overflow-x:\s*hidden|overflow:\s*hidden" -S ./src | head
src/styles/layout.css:44:body { overflow-x: hidden; }
src/components/Gallery.css:3:.gallery { overflow-x: auto; }

Ce que signifie la sortie : Le body a overflow-x: hidden. Ça peut être acceptable, mais c’est une cause fréquente de bugs « impossible de scroller la galerie » quand combiné avec d’autres contraintes de mise en page.

Décision : Si la galerie ne défile pas sur certains appareils, auditez les règles globales d’overflow et le dimensionnement des conteneurs.

Tâche 12 : Chercher un redimensionnement accidentel du conteneur de scroll (unités viewport, toolbars dynamiques)

cr0x@server:~$ rg -n "100vw|100vh|dvh|svh|lvh" ./src/styles | head
src/styles/gallery.css:12:.gallery { width: 100vw; }
src/styles/page.css:8:.page { min-height: 100vh; }

Ce que signifie la sortie : width: 100vw peut inclure la largeur de la barre de défilement sur certaines plateformes, provoquant un overflow horizontal subtil et une dérive de snap.

Décision : Préférez width: 100% pour les conteneurs, et gérez le padding explicitement. Si vous avez besoin du dimensionnement viewport, testez le comportement des barres et des toolbars mobiles dynamiques.

Tâche 13 : Valider que vous n’avez pas introduit de coûts de paint énormes (ombres partout)

cr0x@server:~$ rg -n "box-shadow:|filter:|backdrop-filter:" ./src/styles | head -n 12
src/styles/cards.css:18:.card { box-shadow: 0 24px 80px rgba(0,0,0,0.22); }
src/styles/hero.css:9:.hero { backdrop-filter: blur(18px); }

Ce que signifie la sortie : Grandes ombres floues et backdrop-filter sont coûteux en peinture, surtout pendant le défilement.

Décision : Réduisez le blur/étendue des ombres, retirez backdrop-filter dans les contextes de scroll, ou confinez les effets à des couches non défilantes.

Tâche 14 : Confirmer que le build n’a pas supprimé les propriétés préfixées ou de fallback (bizarreries Safari)

cr0x@server:~$ node -p "process.versions.node"
22.11.0

Ce que signifie la sortie : Vous avez un environnement Node moderne. Si votre pipeline CSS est aussi moderne, il pourrait dépendre de fonctions que vos navigateurs cibles ne supportent pas sans fallbacks.

Décision : Assurez-vous que votre CSS est testé contre votre matrice de navigateurs supportés. Si Safari est dans le scope, validez le comportement sur Safari réel, pas seulement des suppositions « WebKit-ish ».

Tâche 15 : Auditer le nombre d’items dans la galerie (trop de slides = pression mémoire)

cr0x@server:~$ python3 - <<'PY'
from bs4 import BeautifulSoup
html = open("gallery.html","r",encoding="utf-8").read()
s = BeautifulSoup(html,"html.parser")
print("cards:", len(s.select(".card")))
PY
cards: 4

Ce que signifie la sortie : L’exemple est petit. Les pages réelles ont souvent 30+ items.

Décision : Si vous avez beaucoup de slides avec du contenu lourd, envisagez pagination, moins d’items ou une stratégie de rendu — mais testez le blanchiment avec le snap.

Erreurs courantes : symptômes → cause racine → correction

1) « Ça ne snappe pas du tout »

  • Symptômes : Le défilement horizontal fonctionne, mais il n’atterrit jamais proprement sur les éléments.
  • Cause racine : scroll-snap-type n’est pas sur le vrai conteneur de scroll, ou le conteneur ne défile pas (pas d’overflow).
  • Correction : Placez overflow-x: auto et scroll-snap-type: x ... sur le même élément. Assurez-vous que sa largeur est contrainte pour que l’overflow existe.

2) « Ça snappe à des positions bizarres à mi-chemin »

  • Symptômes : Les éléments s’alignent de façon inconsistante ; parfois on voit la moitié de la carte suivante.
  • Cause racine : Le layout visuel ne correspond pas à la géométrie des boîtes (marges négatives, transforms, pseudo-éléments affectant les bords perçus).
  • Correction : Supprimez les marges négatives des éléments de snap ; utilisez gap et du padding. Alignez les boîtes des items de snap sur ce que voit l’utilisateur.

3) « Le défilement de la page reste coincé dans la galerie »

  • Symptômes : Sur mobile, le défilement vertical devient difficile quand le doigt passe sur la galerie.
  • Cause racine : Le conteneur horizontal capture l’intention tactile ; chaînage de scroll imbriqué ; touch-action trop agressif.
  • Correction : Utilisez overscroll-behavior-x: contain et envisagez de retirer ou détendre touch-action. Rendez la galerie moins haute pour qu’elle intercepte moins le scroll vertical.

4) « Le snap atterrit sous l’en-tête »

  • Symptômes : Le début d’une carte est caché sous une UI sticky.
  • Cause racine : Pas de scroll-padding ou scroll-margin pour tenir compte des overlays.
  • Correction : Définissez scroll-padding-inline ou scroll-padding-top sur le conteneur de scroll selon l’axe.

5) « C’est fluide sur desktop, affreux sur téléphones »

  • Symptômes : Saccades mobiles, flashs blancs, apparition d’images retardée lors du snapping.
  • Cause racine : Images trop lourdes et temps de décodage ; rendu paresseux trop agressif ; peinture lourde.
  • Correction : Utilisez des images responsives, déclarez dimensions/aspect-ratio, simplifiez ombres/filtres et évitez de masquer le rendu des éléments proches hors-écran.

6) « Ça saute quand la barre de défilement apparaît »

  • Symptômes : Sur desktop, la galerie se décale légèrement puis l’alignement snap est décalé.
  • Cause racine : La gouttière de la barre de défilement change la mise en page, souvent à cause des paramètres OS ou des barres qui n’apparaissent qu’au survol.
  • Correction : Utilisez scrollbar-gutter: stable quand c’est supporté ; sinon assurez-vous que la taille du conteneur ne dépend pas de 100vw.

7) « Safari ignore mon joli comportement »

  • Symptômes : Réglage de snap différent de Chromium/Firefox ; effet caoutchouc bizarre.
  • Cause racine : Différences de moteurs et physique d’inertie ; souvent overflow imbriqué et transforms.
  • Correction : Simplifiez : moins de régions de scroll imbriquées, évitez les transforms sur les parents du conteneur de scroll, envisagez proximity, retirez le défilement fluide pour les builds affectés.

8) « Les utilisateurs clavier n’atteignent pas le contenu »

  • Symptômes : Tab n’entre pas dans les slides, ou l’anneau de focus disparaît hors de la vue.
  • Cause racine : Pas d’éléments focalisables ; styles de focus retirés ; clipping d’overflow sans comportement de scroll-to-focus.
  • Correction : Assurez-vous qu’il y a du contenu focalisable ; ajoutez du :focus-visible ; évitez outline: none sans remplacement.

Listes de contrôle / plan pas-à-pas

Pas-à-pas : construire une galerie snap qui ne vous embarrassera pas plus tard

  1. Choisissez le modèle de mise en page. Utilisez CSS Grid en colonnes pour les cartes. Évitez les floats astucieux, marges négatives et transforms sur le conteneur de scroll.
  2. Créez un vrai conteneur de scroll. overflow-x: auto, largeur contrainte par le parent, pas de règles globales d’overflow en conflit.
  3. Définissez le comportement de snap. Commencez avec scroll-snap-type: x proximity sauf si vous avez une vraie raison pour mandatory.
  4. Définissez l’alignement des items. Par défaut scroll-snap-align: start.
  5. Ajoutez un espacement prévisible. Utilisez gap et padding du conteneur ; définissez scroll-padding-inline en conséquence.
  6. Rendez-le utilisable au clavier. Assurez-vous d’avoir des éléments focalisables ; ajoutez :focus-visible.
  7. Respectez la réduction du mouvement. N’activez le défilement fluide que si la préférence le permet.
  8. Stabilisez les médias. Déclarez dimensions/aspect-ratio des images ; évitez le chargement tardif qui change la taille des cartes.
  9. Surveillez les overflow imbriqués. N’ajoutez pas de régions scrollables à l’intérieur de slides à moins d’en avoir vraiment besoin.
  10. Testez sur différents inputs. Souris, trackpad, tactile et au moins un Safari.
  11. Déployez derrière un flag si risqué. Surtout pour les pages marketing à fort trafic où « petites régressions UX » coûtent cher.
  12. Mesurez et itérez. Surveillez CLS, latence d’interaction et retours utilisateurs corrélés à l’appareil/navigateur.

Checklist pré-déploiement (rapide)

Géométrie : le conteneur scroll fonctionne ; les enfants ont snap align ; pas de marges négatives sur les items de snap.
UX : barre de défilement visible ou alternative existante ; anneau de focus visible ; ancres fonctionnelles si fournies.
Perf : images dimensionnées et compressées ; pas d’ombres/filters géants ; pas de blanchiment lors de flicks rapides.
Compatibilité : testé sur Safari et au moins un appareil Android ; réduction du mouvement vérifiée.

FAQ

1) Dois-je utiliser mandatory ou proximity ?

Par défaut, préférez proximity sauf si la galerie est explicitement à étapes (photos produit, onboarding). mandatory augmente les plaintes de « blocage » si vos points de snap sont denses ou vos cartes étroites.

2) Pourquoi ça se comporte différemment sur trackpad et sur roulette souris ?

Parce que le flux d’entrée est différent : les trackpads envoient des deltas continus et haute-résolution ; les roulettes envoient des ticks grossiers. Le snapping intervient après que le défilement se soit stabilisé, et le timing de « stabilisation » diffère selon le dispositif.

3) Puis-je construire un carrousel complet (points, suivant/précédent, autoplay) sans JS ?

Vous pouvez construire la navigation avec des ancres et le styliser joliment. L’autoplay sans JS est une mauvaise idée pour l’accessibilité et le contrôle utilisateur de toute façon. Pour des « points », utilisez des liens vers les IDs des slides et gardez les choses simples.

4) Est-ce que scroll-behavior: smooth rend le défilement tactile plus fluide ?

Pas vraiment. Il affecte surtout le défilement programmatique et certaines actions pilotées par l’UA. La dynamique d’inertie tactile est de la physique de plateforme. Ne comptez pas sur smooth pour corriger le jank.

5) Mes slides ont du padding — pourquoi les positions de snap sont décalées de quelques pixels ?

Généralement arrondis et box-sizing. Privilégiez l’alignement sur un bord stable (start), faites correspondre le padding du conteneur avec scroll-padding, et évitez les largeurs fractionnaires quand possible (par ex. 33.333% plus de grands gaps).

6) Comment empêcher la page de défiler quand l’utilisateur interagit avec la galerie ?

Utilisez overscroll-behavior-x: contain sur la galerie. Réfléchissez à touch-action: pan-x avec prudence ; il améliore l’intention horizontale mais peut rendre le scroll vertical plus difficile sur la galerie.

7) Pourquoi certains éléments « sautent » quand je flick rapidement ?

La vélocité peut emporter le défilement au-delà de plusieurs points de snap. Si vous avez besoin que chaque élément soit un arrêt dur, essayez scroll-snap-stop: always sur les items — mais testez ; ça peut donner une sensation restrictive.

8) Dois-je masquer la barre de défilement pour un look plus propre ?

Seulement si vous la remplacez par quelque chose d’aussi clair et accessible. Sinon vous supprimez un affordance et le remplacez par une impression. Si vous devez la cacher, assurez-vous que l’utilisation clavier et tactile est excellente.

9) Le scroll snap peut-il causer un layout shift (CLS) ?

Le scroll snap ne crée pas le CLS en soi. Mais il rend les changements de layout plus visibles parce que l’utilisateur s’attend à un atterrissage stable. Le CLS vient généralement d’images sans espace réservé, de swaps de polices tardifs ou d’injections de contenu dynamique.

10) Quelle est la façon la plus simple de le rendre responsive ?

Utilisez grid auto-columns en pourcentages et ajustez aux breakpoints. Exemple : 85% sur petits écrans (une carte majoritairement visible), 45% sur grands écrans (deux cartes visibles environ). Puis ajustez gap et scroll-padding.

Conclusion : quoi faire ensuite

Si vous voulez des galeries horizontales fluides sans JavaScript, le scroll snap est le bon primitive. Mais ce n’est pas magique. Traitez-le comme toute autre fonctionnalité en production : gardez la géométrie honnête, stabilisez la mise en page et mesurez les goulots au lieu de deviner.

Étapes à entreprendre cette semaine :

  1. Refactorez votre galerie en un conteneur de scroll unique et évident avec des boîtes d’enfants prévisibles (grid + gap + padding).
  2. Passez à proximity sauf si le produit a vraiment besoin d’un snap strict, et ajoutez scroll-padding-inline pour correspondre à l’espacement du design.
  3. Réparez les médias : déclarez dimensions/aspect-ratio et réduisez les octets images jusqu’à ce que les mobiles arrêtent de surchauffer.
  4. Exécutez le playbook de diagnostic rapide sur Safari et un téléphone réel avant de débattre des « bizarreries de navigateur ».
  5. Si la page est à fort impact, déployez derrière un flag et préparez un rollback. Ce n’est pas du pessimisme ; c’est la manière de préserver vos weekends.

Conçu en partant du principe que les systèmes de production sont réels, que les navigateurs ont des opinions, et que les utilisateurs trouveront toujours l’appareil que vous n’avez pas testé.

← Précédent
Problèmes de délivrabilité Gmail/Outlook : les vérifications qui comptent en 2025
Suivant →
Pourquoi les pilotes d’imprimante sont devenus énormes : la surcharge que personne n’a demandée

Laisser un commentaire