Le cadre .demo-preview (flex centré, 100% × 280px, overflow: hidden, position: relative, fond rgb(10, 10, 15), texte blanc, sans-serif) contient la boîte .progress-scroll-container : 100% × 100%, overflow-y: auto, overflow-x: hidden, et la ligne qui fait l'effet, scroll-timeline: --progressScroll y — une timeline de défilement nommée, dont la progression va de 0 en haut de la boîte à 1 sur son dernier pixel défilable. Dans la boîte, la piste .slr-progress-bar-track (position: sticky; top: 0, 4px de haut, blanc à 10 %, z-index: 5) porte la jauge .slr-progress-bar-fill : dégradé à 90° #6366f1 → #d946ef → #f59e0b, transform-origin: left, transform: scaleX(0), animation: progressGrow linear both et animation-timeline: --progressScroll. Dessous, .progress-content (padding 16px 12px) : quatre h3 de 14,4 px en #a78bfa, sept p de 12,8 px en blanc à 70 %. Hors de la boîte, span.slr-scroll-hint « Scrollez pour voir l'effet », absolu à 8 px du bas du cadre, 11,2 px, blanc à 40 %, rebondit de 0 à −4 px toutes les 2 s. Le champ JavaScript est vide.
@keyframes progressGrow va de scaleX(0) à scaleX(1). Avec animation-timeline, le temps de l'animation n'est plus l'horloge mais la position de défilement (durée calculée auto) ; linear rend la largeur exactement proportionnelle, both tient les deux extrémités. Mesuré dans Chromium 151, page vierge, fond blanc comme sombre : scrollTop 0 / 31 / 61 / 92 / 122 px → scaleX 0 / 0,254 / 0,5 / 0,754 / 1, soit scrollTop ÷ course au millième ; WebKit 26.5 donne les mêmes valeurs, et la piste reste à 0 px du haut de la boîte à toutes les positions. La course dépend de la largeur : à 1 264 px de large, les quatre chapitres n'occupent que 402 px, la course fait 122 px et un seul cran de molette de 120 px porte la jauge à 0,984 ; à 374 px de large, le texte fait 669 px, la course 389 px, et les crans donnent 0,308, 0,617, 0,925 puis 1. La jauge mesure la course de la boîte, pas un temps de lecture : un texte court dans une colonne large se « lit » en un cran.
Face à scroll(nearest) (Progress Bar, fx-0555), la différence est la résolution. Une timeline anonyme lie l'élément animé à son ancêtre défilant le plus proche : la jauge doit vivre dans la boîte. Une timeline nommée se résout par son nom : la jauge trouve --progressScroll sur n'importe quel ancêtre, et avec timeline-scope: --progressScroll posé sur un ancêtre commun, elle peut être sœur de la boîte — dans un en-tête fixe, une barre de titre. Mesuré, piste sortie de la boîte et placée avant elle : mêmes valeurs 0 / 0,254 / 0,5 / 0,754 / 1 dans Chromium 151 et WebKit 26.5. Deux copies collées sur la même page restent indépendantes (mesuré : seconde boîte à 60 % → 0,599, la première à 0) : chaque boîte déclare sa timeline, chaque jauge se lie à la plus proche, ni id ni script. Préfixe slr- incomplet (progress-scroll-container, progress-content, @keyframes progressGrow / hintBounce sans préfixe) : vérifiez qu'une feuille existante ne les déclare pas déjà.
CSS seul : scroll-timeline (nommée), animation-timeline, position: sticky, overflow-y, un dégradé, deux @keyframes. Zéro dépendance, zéro JavaScript. Animations pilotées par le défilement : Chrome et Edge 115+, Safari et iOS 26+ (mesuré dans WebKit 26.5, valeurs identiques à Chromium) ; timeline-scope, pour sortir la jauge de la boîte : Chrome 116+, Safari 26. Firefox : aucune version. Mesuré dans Firefox 153 : CSS.supports('animation-timeline: scroll()') répond faux, la déclaration est ignorée, animation: progressGrow linear both redevient une animation d'horloge de 0 s dont both applique l'image-clé finale aussitôt — la barre est pleine dès le chargement (scaleX 1, 1 264 px sur 1 264) et le reste à toutes les positions ; le texte est lisible et la boîte défile. Rien n'est caché, la barre ne renseigne simplement pas.
Le code vendu n'a pas de repli : dans Firefox, Chrome avant 115 et Safari avant 26, la barre est pleine dès le chargement et n'évolue plus, le texte reste lisible et la boîte défile. Pour une piste vide plutôt qu'une barre à 100 %, ajoutez @supports not (animation-timeline: scroll()) { .slr-progress-bar-fill { animation: none } } — mesuré dans Firefox 153 : scaleX(0) à toutes les positions, sans effet dans Chromium. Pour une progression réelle partout, il faut le JavaScript d'Introduction (fx-0575).