Le cadre .demo-preview (280 px, overflow: hidden, fond #0a0a0f, texte blanc, sans-serif) contient la boîte défilante .sdc-scroll-container (100% × 280px, overflow-y: auto, scroll-behavior: smooth), le plateau .sb-fadeup-content (900 px, padding: 60px 20px, colonne flex à gap: 24px) et cinq .sb-fadeup-box — cartes sombres de 24 px de padding, un h4 et un p chacune. Mesuré dans Chromium 151, page vierge : blocs de 86 px posés à 60, 170, 280, 390 et 500 px, course de 620 px. L'indice « Scrollez ↓ » (.sdc-scroll-hint) s'accroche au cadre à 8 px du bas et pulse de .3 à .7 toutes les 2 s. Le champ JavaScript est vide.
Toute la mécanique est sur .sb-fadeup-box : opacity: 0, transform: translateY(60px), animation: sbFadeUp linear both, animation-timeline: view(), animation-range: entry 0% entry 50%. view() remplace l'horloge par la position du bloc dans le conteneur défilant le plus proche, ici .sdc-scroll-container. La plage entry va du moment où le bord haut du bloc franchit le bas de la boîte (0 %) à celui où le bloc est entièrement dedans (100 %) ; entry 0% entry 50% n'en garde que la première moitié : pour un bloc de 86 px, l'animation vit sur 43 px de défilement. L'unique image-clé, to { opacity: 1; transform: translateY(0) }, et le remplissage both figent l'état de départ avant la plage et l'état final après. Mesuré : au repos, les blocs 1 et 2, déjà dans la boîte, sont à 1 ; le bloc 3, dont le bord haut est pile au bord bas, est à 0 — 0,93 et 4,2 px trop bas à 40 px de défilement, 1 et 0 à 80. Le bloc 4 entre à 110 px : 0,23 à 120, 1 à 160 ; le bloc 5 entre à 220 : 0,47 à 240, 1 à 280. Retour en haut : les blocs 3 à 5 repassent à 0, la timeline se lit dans les deux sens.
La valeur de l'effet est dans ce qu'il ne fait pas : aucun :nth-child, aucun animation-delay, aucun compteur. Chaque bloc porte sa propre timeline, calée sur sa propre hauteur ; trois ou trois cents entrées obéissent à la même règle, et un bloc ajouté après coup s'anime aussi (mesuré : sixième bloc inséré par script, plateau en height: auto, 0 → 1 entre 300 et 400 px de défilement, rien à réinitialiser). Un délai en millisecondes n'a aucun effet sur une timeline de défilement (mesuré avec animation-delay: 200ms : mêmes valeurs), ce qui rend vaines les cascades de Stagger Cards (fx-0540) et Stagger Reveal CSS (fx-0552). Pour un bloc plus haut que la boîte, la plage entry s'arrête quand il la couvre en entier : mesuré, un bloc de 600 px dans la boîte de 280 s'anime sur 140 px, la moitié de la boîte — la règle est la moitié du plus petit des deux, bloc ou boîte. Le plateau de 900 px est un artifice de démonstration (les blocs finissent à 586 px, au-delà de 306 px de défilement plus rien n'entre) : retirez cette hauteur, et si la liste est dans le flux du document, view() prend la fenêtre pour boîte (mesuré : mêmes 43 px par bloc).
CSS seul, zéro dépendance, zéro JavaScript. Deux propriétés récentes portent tout : animation-timeline: view() et animation-range avec la plage nommée entry — Chrome et Edge 115 (juillet 2023), Safari 26 (septembre 2025 ; mesuré dans WebKit 26.5 : mêmes valeurs que Chromium). Firefox : aucune version. Mesuré dans Firefox 153 : CSS.supports('animation-timeline: view()') répond faux, les deux déclarations sont ignorées et animation: sbFadeUp linear both tourne sur l'horloge du document avec une durée de 0 s, terminée au chargement : les cinq blocs sont à opacity: 1 et translateY(0) dès la première image, avant comme après défilement, la boîte défile ses 620 px et l'indice pulse. Un visiteur Firefox voit une liste à plat, complète et lisible — rien de caché, rien qui bouge. Aucun @supports n'est nécessaire parce que l'image-clé finale est l'état visible ; si vos images-clés changent, ajoutez @supports not (animation-timeline: view()) { .sb-fadeup-box { animation: none; opacity: 1; transform: none } }.