Kodokon kodokon.com

Interactions modernes : scroll-snap, view transitions

Orchestrez des défilements magnétiques, des transitions d'état animées par le navigateur et un respect rigoureux de prefers-reduced-motion.

11 min · 3 questions

Ouvrir cette leçon dans Kodokon

Le scroll snap repose sur deux acteurs : le conteneur définit un snapport (sa zone visible, réduite par scroll-padding) et une stratégie via scroll-snap-type - proximity n'aimante que près d'un point d'accroche, mandatory garantit de toujours finir sur un point, même après un défilement programmatique. Les enfants déclarent leur zone d'alignement avec scroll-snap-align (ajustable par scroll-margin). Subtilité méconnue : lors d'un swipe rapide, le navigateur peut légitimement sauter plusieurs points ; scroll-snap-stop: always l'oblige à s'arrêter sur chacun, indispensable pour un carrousel de type story.

CSS
.carousel {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: 1rem;
}
.carousel > article {
  flex: 0 0 80%;
  scroll-snap-align: center;
  scroll-snap-stop: always;
}
Carrousel magnétique : chaque slide se centre et aucun swipe ne peut en sauter une.

L'API View Transitions anime un changement d'état DOM sans bibliothèque. document.startViewTransition(callback) capture un instantané visuel de l'état courant, exécute votre callback (qui mute le DOM), puis anime entre ancien et nouvel état. Sous le capot, le navigateur construit un arbre de pseudo-éléments au-dessus de la page : ::view-transition contient des ::view-transition-group, chacun abritant ::view-transition-old (l'instantané, un contenu remplacé comparable à une image) et ::view-transition-new (la représentation vivante). Par défaut : fondu croisé. La page est non interactive pendant la transition.

JAVASCRIPT
function applyUpdate(updateDom) {
  if (!document.startViewTransition) {
    updateDom();
    return;
  }
  document.startViewTransition(() => updateDom());
}
Amélioration progressive : sans support, la mise à jour s'applique simplement sans animation.

Le vrai pouvoir vient de view-transition-name : un élément nommé est extrait dans son propre groupe de transition. Si un élément portant le même nom existe avant et après, le navigateur interpole automatiquement position et taille entre les deux - l'effet de morphing d'une vignette vers son plein écran, sans une ligne de JavaScript d'animation. Chaque nom doit être unique dans la page à un instant donné, sous peine d'abandon de la transition. Les pseudo-éléments se stylent avec des animations CSS ordinaires.

CSS
.hero-thumb { view-transition-name: hero; }

::view-transition-old(hero),
::view-transition-new(hero) {
  animation-duration: 0.4s;
  animation-timing-function: ease-in-out;
}
Le groupe nommé hero est intercepté et sa durée d'animation personnalisée.

Reste l'obligation d'accessibilité : prefers-reduced-motion: reduce signale un utilisateur sensible au mouvement (troubles vestibulaires, migraines). Réduire ne veut pas dire supprimer : un fondu d'opacité reste acceptable, ce sont les grands déplacements, zooms et parallaxes qu'il faut neutraliser. Vous connaissez déjà la surcharge globale défensive en CSS (durées quasi nulles, vue au module précédent) ; les view transitions ajoutent un second front : côté JavaScript, interrogez window.matchMedia("(prefers-reduced-motion: reduce)") avant de lancer une transition ou un scroll animé.

JAVASCRIPT
const reduceMotion = window.matchMedia(
  '(prefers-reduced-motion: reduce)'
).matches;

if (reduceMotion || !document.startViewTransition) {
  updateDom();
} else {
  document.startViewTransition(() => updateDom());
}
Le garde-fou JavaScript : la mise à jour s'applique sans morphing si l'utilisateur demande moins de mouvement.

Quiz de validation

Vérifiez que vous avez bien retenu les points clés de cette leçon.

  1. À quoi sert scroll-snap-stop: always sur un élément d'un carrousel ?
    • À désactiver l'inertie du défilement tactile
    • À forcer l'arrêt sur chaque point d'accroche, même lors d'un swipe rapide
    • À bloquer le défilement dans l'axe perpendiculaire
  2. Que capture document.startViewTransition() avant d'exécuter votre callback ?
    • Un clone profond du DOM courant
    • Un instantané visuel de l'ancien état, rejoué dans ::view-transition-old
    • Le HTML sérialisé de la page entière
  3. Comment respecter prefers-reduced-motion avant de lancer une view transition depuis JavaScript ?
    • C'est impossible : cette préférence n'est lisible qu'en CSS
    • Tester window.matchMedia('(prefers-reduced-motion: reduce)').matches et appliquer la mise à jour sans startViewTransition le cas échéant
    • Passer l'option { motion: 'reduce' } à startViewTransition