Scroll✨ Premium

Text Highlight on Scroll

Soixante-quatre mots à 15 % d'opacité qu'un écouteur scroll allume un à un : index = ⌊scrollTop ÷ course × 64⌋, soit 3,4 px de défilement par mot dans un bloc de 280 px, fondu de 300 ms, le mot courant en violet. Rien n'est allumé avant le premier scroll, et à cette hauteur les mots s'allument surtout après être sortis du cadre.

JavaScriptCSSTypographie

Vous aurez accès à la démo interactive avec un compte gratuit.

Cet effet fait partie d'Effect.Labs — 811 effets vanilla, une partie gratuite, une partie premium. Scroll comprend 70 effets, dont 12 gratuits. Découvrir la catégorie →

3 exemples d'usage

Contrat de prestation signable en ligne — les clauses s'allument sous la lecture — Text Highlight on Scroll exemple 1

① Contrat de prestation signable en ligne — les clauses s'allument sous la lecture

QuandParcours de signature électronique : le signataire fait défiler le contrat dans un visualiseur, une colonne suit sa progression et le bouton « Signer » se débloque à la fin.
PourquoiL'index calculé sur scrollTop est un nombre que le reste de la page peut lire : le même événement alimente le pourcentage de lecture, coche chaque article franchi et libère le bouton à t = 1 — une trace visuelle de lecture linéaire, sans seuils prédéfinis. Ce que fx-0591, livré sans script, ne fournit pas.
RéglagesBloc de 294 px, padding-top 112 px et espaceur réduit à 162 px (bloc − paddings) : la course égale la hauteur du texte, 416 px pour 68 mots (6,1 px par mot), et le mot courant reste entre 53 et 127 px du haut au lieu de sortir par le bord ; corps 13,5 px, interligne 1,85 ; pourcentage, articles cochés et bouton « Signer » lus sur le même ratio.
Retranscription d'audience avec notes en marge — Text Highlight on Scroll exemple 2

② Retranscription d'audience avec notes en marge

QuandPlateforme juridique : la retranscription d'une audience défile à gauche, les notes du greffe ou de l'avocat apparaissent à droite au moment où le passage annoté est atteint.
PourquoiChaque note est attachée à un index de mot ; l'écouteur qui recalcule g à chaque pixel de défilement permet d'afficher la note exactement quand le mot cible devient le mot courant — un second élément, hors du bloc de texte, piloté par la même lecture de scrollTop.
RéglagesAccent ambre #fbbf24 (11,8:1) en surligneur, corps 13 px, interligne 1,8, transition: color .2s ; bloc de 322 px, padding-top 118 px, espaceur 184 px : 311 px de course pour 58 mots (5,4 px par mot). Chaque note porte en data-at l'index du mot qui l'affiche.
Manifeste « Nos valeurs » projeté — lecture cérémonielle mot à mot — Text Highlight on Scroll exemple 3

③ Manifeste « Nos valeurs » projeté — lecture cérémonielle mot à mot

QuandPage valeurs d'un cabinet de conseil, projetée lors d'un onboarding : quatre phrases en grands caractères, le présentateur fait défiler au rythme de sa voix.
PourquoiLe surlignage suit la main de l'orateur et non un minuteur : il peut s'arrêter, revenir, accélérer, le mot courant reste celui qu'il prononce. Une animation CSS pilotée au scroll avancerait par seuils fixes ; ici c'est la position exacte, relue à chaque événement, qui désigne le mot.
Réglagesfont-size 26 px, graisse 700, line-height: 1.5, éteint à .22 pour que la suite se devine, accent #c084fc (7,5:1) ; bloc de 316 px, padding-top 150 px, espaceur 126 px : 195 px de course pour 29 mots, 6,7 px par mot — un cran de molette de 40 px avance de six mots.

Comment ça marche

Le HTML vendu est un bloc .scroll-container#highlight-scroll de 280px de haut, overflow: hidden auto et scroll-behavior: smooth, qui contient .highlight-container#highlight-text (padding: 24px 20px) avec 64 span.highlight-word en ligne — .95rem (15,2 px), graisse 500, line-height: 2 (30,4 px) —, puis un espaceur <div style="height: 300px"> et une étiquette .scroll-hint-label « Scrollez pour explorer » qui pulse de .3 à .7 d'opacité en 2 s (@keyframes pulseHint). Trois états de couleur, avec transition: color .3s : éteint rgba(255,255,255,.15), .lit blanc, .accent #a855f7. Aucune variable CSS, aucune dépendance ; le texte vendu est écrit sans accents (« defilement », « revele », « cree »).

Le script (12 lignes, un seul écouteur scroll sur le bloc) calcule à chaque événement t = scrollTop / (scrollHeight − clientHeight) puis g = Math.floor(t × 64) : les mots d'index inférieur à g reçoivent lit, le mot g reçoit accent, tous les autres sont remis à zéro. Mesuré dans Chromium sur un bloc de 784 px de large : 5 lignes, scrollHeight 500 px, course de 220 px, soit 3,44 px de défilement par mot ; un cran de molette de 40 px bascule 11 à 12 mots d'un coup, que le fondu de 300 ms étale en vague. La granularité est le pixel entier, pas le sous-pixel : scrollTop = 10.11 se relit 10 et 10.51 se relit 11, en DPR 1 comme en DPR 2. À t = 1, g = 64 : tout est allumé et l'accent disparaît. Aucun calcul initial : avant le premier scroll, 0 mot allumé, 0 accent. Et comme scroll-behavior: smooth s'applique aussi aux écritures de scrollTop, une position posée par script est atteinte en animation — relue juste après l'écriture, c'est l'ancienne valeur.

L'index suit la course, pas la géométrie du texte : avec l'espaceur de 300 px, la course (220 px) dépasse la hauteur du texte (200 px). Mesuré à 784 px de large, position par position : le mot accentué n'est dans le cadre que pour 76 des 221 positions, toujours entre −17 et +29 px du bord haut ; il en sort à 47 px de défilement (13ᵉ mot), n'y revient plus après 107 px, et dès 80 px plus aucun mot allumé n'est visible — seules les lignes encore éteintes restent à l'écran, et les 52 derniers pixels de course défilent sans un mot dans le cadre. À 390 px de large (10 lignes, course 372 px, 5,8 px par mot) : 178 positions sur 373, sortie à 108 px. Autrement dit, à 280 px de haut, les mots s'allument surtout après être sortis du cadre ; l'effet tient sa promesse dans un bloc nettement plus haut que le texte, ou avec la course calée sur la hauteur du texte (cf. Personnaliser). Le code est aussi mono-instance : const au niveau global (collé deux fois : SyntaxError: Identifier 'highlightScroll' has already been declared), getElementById unique et querySelectorAll sur toute la page — avec deux blocs, mesuré, le premier est entièrement allumé à mi-course (64 mots sur 128 comptés) et le second reste inerte. Rien n'est jamais retiré, mais il n'y a rien à arrêter : pas de boucle, un écouteur sur un élément.

Accessibilité

  • prefers-reduced-motion absent du code : mesuré sous émulation, la transition de 300 ms et la pulsation de 2 s de l'étiquette continuent. Le mouvement est piloté par le lecteur (rien ne bouge seul, hormis l'étiquette) ; ajoutez tout de même @media (prefers-reduced-motion: reduce) { .scroll-hint-label { animation: none } .highlight-word { transition: none } .scroll-container { scroll-behavior: auto } } — le défilement doux est lui aussi une animation.
  • Contraste : les mots éteints sont du texte à lire, pas un décor — rgba(255,255,255,.15) sur #0a0a0f mesure 1,48:1, illisible tant que le mot n'est pas atteint (et rien n'est allumé avant le premier scroll). Allumé, 19,75:1 ; accent #a855f7, 4,99:1 — conforme à 15,2 px. Passez l'état éteint à .45 (4,5:1) si le texte doit pouvoir se lire en avance, ou assumez le masquage et donnez le contenu ailleurs. Sur fond clair, le code ne tient pas : 1,0:1 éteint, 3,96:1 pour l'accent. L'étiquette « Scrollez pour explorer » culmine à 2,6:1 et pulse entre 1,2 et 1,8:1 : c'est une consigne, rendez-la lisible ou retirez-la.
  • Clavier et tactile : mesuré dans Chromium, Tab donne le focus au bloc (défileurs focalisables au clavier) et chaque flèche bas déplace de 40 px, soit 11 à 12 mots. Firefox et Safari ne focalisent pas un bloc défilant sans tabindex="0" sur #highlight-scroll — ajoutez-le, avec un :focus-visible visible. Molette et défilement tactile imbriqué fonctionnent. La barre de 4 px stylée par ::-webkit-scrollbar reste native sur Firefox (scrollbar-width: thin).
  • Lecteurs d'écran et sans JavaScript : 64 span en ligne séparés par des espaces dans un div — le texte est lu comme une phrase, l'état allumé (classes seules) n'est pas annoncé, ce qui convient à un surlignage décoratif ; mettez le texte dans un <p> et restituez les accents. Sans script, ou tant que personne ne défile, tout le texte reste à 15 % : prévoyez un <noscript> qui force la couleur pleine, ou déclenchez un premier calcul au chargement (highlightScroll.dispatchEvent(new Event('scroll'))).

Compatibilité navigateur

Le script tient en ES2015 (const, fonctions fléchées, NodeList.forEach, classList.remove(a, b)) et le CSS repose sur une seule propriété récente : la forme à deux valeurs overflow: hidden auto — un navigateur qui ne la comprend pas ignore la déclaration, le bloc ne défile plus et l'effet est mort. Pas d'animation-timeline ni de scroll-timeline : le surlignage vient d'un écouteur scroll classique, il fonctionne donc sur Firefox, qui ne supporte les animations pilotées au scroll en CSS dans aucune version. Zéro dépendance.

Chrome 68+✓ Complet
Firefox 61+✓ Complet (barre de défilement native)
Safari 12.1+✓ Complet (défilement doux ignoré avant 15.4)
Edge 79+✓ Complet
Mobile iOS 12.2+✓ Complet (défilement tactile du bloc)
Android Chrome✓ Complet

Sans JavaScript, le bloc défile mais tout le texte reste à 15 % d'opacité : aucun mot ne s'allume, seule l'étiquette pulse. Sans overflow: hidden auto (Chrome < 68, Firefox < 61, Safari < 12.1), le bloc s'étire à la hauteur de son contenu et rien ne défile.

Le code

Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :

index.html — structure
<div class="scroll-container" id="highlight-scroll">
  <div class="highlight-container" id="highlight-text">
    <span class="highlight-word">…</span>
  </div>
  <div style="height: 300px"></div>
</div>
<span class="scroll-hint-label">…</span>
🔒 Débloquer le code complet — à partir de 2,99 € le 1er mois

HTML + CSS + JS complet, prêt à coller — avec des centaines d'effets premium.

Personnaliser

Options passées à l'API ou attributs data-* :

Option / propriétéDéfautEffet
Hauteur du bloc (CSS — .scroll-container { height: 280px }) 280 px Fenêtre de lecture. Plus le bloc est haut par rapport au texte, plus le mot allumé reste visible : à 280 px pour 200 px de texte, il sort du cadre dès 47 px de défilement. Une section plein écran (100vh) avec un texte de deux ou trois écrans est le cas favorable.
Espaceur de fin (HTML — <div style="height: 300px">) 300 px Allonge la course après le texte : les derniers mots s'allument quand le texte est déjà sorti (52 px de course sans texte à 784 px de large). Réglez-le à hauteur du bloc − padding − une ligne (226 px ici) pour que la dernière ligne s'allume encore dans le cadre, ou montez le padding-top et posez espaceur = bloc − paddings : la course égale alors la hauteur du texte et le mot courant se fixe à la hauteur du padding.
Placement du mot courant (JS — Math.floor(t * i)) proportionnel à la course Pour attacher l'allumage à la position réelle des mots, remplacez le calcul par un test géométrique : lit si w.offsetTop − scrollTop < clientHeight × .5, accent au premier mot qui échoue. Le mot courant reste alors à mi-hauteur quelle que soit la largeur, et l'espaceur ne sert plus qu'à atteindre la dernière ligne.
Couleur éteinte (CSS — .highlight-word { color: rgba(255,255,255,.15) }) blanc à 15 % (1,48:1) Lisibilité du texte non encore atteint. .3 = 2,6:1 (on devine), .45 = 4,5:1 (on lit). Sur fond clair, remplacez par un gris (#9ca3af) — le blanc translucide y disparaît.
Couleurs allumé et accent (CSS — .lit { color: #fff }, .accent { color: #a855f7 }) #fff / #a855f7 L'accent marque le seul mot courant ; #a855f7 tient 4,99:1 sur #0a0a0f, #c084fc 7,5:1, un ambre #fbbf24 11,8:1 fait surligneur. Un accent peut aussi porter un fond : la transition ne couvre que color, ajoutez background-color.
Fondu (CSS — transition: color .3s) 300 ms Durée du passage éteint → allumé. Un cran de molette bascule 11 à 12 mots d'un coup : à .3s ils se fondent en vague, à 0 ils claquent, à .8s la vague traîne derrière le défilement.
Corps et interligne (CSS — font-size: .95rem, line-height: 2) 15,2 px / 30,4 px Fixent le nombre de lignes, donc la hauteur du texte et la densité de mots par pixel de course (3,44 px par mot à 784 px de large, 5,8 px à 390 px). En grands caractères (26 px, interligne 1,5), 29 mots sur 195 px donnent 6,7 px par mot : lecture cérémonielle.
Défilement doux et étiquette (CSS — scroll-behavior: smooth, .scroll-hint-label) smooth / « Scrollez pour explorer » pulsant 2 s Le smooth anime aussi les scrollTop posés par script (relu juste après : l'ancienne valeur) : passez-le à auto si vous pilotez la position. L'étiquette est un reste de catalogue : traduisez-la ou retirez le span et l'animation pulseHint.

FAQ

Parce que l'index du mot courant est Math.floor(scrollTop / course × 64) : il avance avec la course, pas avec la position des lignes. Avec l'espaceur de 300 px et un bloc de 280 px, la course (220 px à 784 px de large) est plus longue que le texte (200 px) et démarre le premier mot en haut du cadre : à chaque pixel défilé, le texte monte d'un pixel alors que le mot courant n'avance que de 0,29 mot — il est rattrapé par le bord dès 47 px, plus jamais visible après 107 px, et dès 80 px on ne voit plus que des mots éteints. Deux remèdes : caler la course sur la hauteur du texte (padding-top égal à la hauteur de lecture voulue, espaceur = bloc − paddings), ou remplacer le calcul par un test géométrique par mot (offsetTop − scrollTop < clientHeight × .5). Les trois scènes de cette page utilisent le premier remède : le mot courant y reste visible sur toute la course, entre 53 et 129 px du haut dans les deux premières, entre 106 et 156 px dans la troisième.
Le rendu visé est le même — mots à 15 %, blanc quand lus, un accent violet — mais le code vendu de fx-0591 contient le HTML (33 mots en inline-block, une barre de progression #ssHighlightFill) et les trois états CSS, avec un champ JavaScript vide : rien n'y ajoute lit ni ne remplit la barre. fx-0579 livre l'écouteur de 12 lignes qui fait l'effet, avec des mots en inline (la césure suit le texte, pas le mot) et un fondu de .3 s au lieu de .2 s. Ni l'un ni l'autre ne repose sur animation-timeline ; le surlignage de fx-0579 fonctionne donc sur Firefox, qui n'implémente pas les animations CSS pilotées au scroll.
Pas tel quel. Le script déclare highlightScroll et highlightWords en const au niveau global : collé une seconde fois, il s'arrête sur SyntaxError: Identifier 'highlightScroll' has already been declared. Et même enveloppé dans une fonction, il vise getElementById("highlight-scroll") — un seul élément — et compte tous les .highlight-word de la page : mesuré avec deux blocs, le premier est entièrement allumé à mi-course (64 mots sur 128 comptés) et le second ne réagit jamais. Pour plusieurs instances, enveloppez le code dans document.querySelectorAll('.scroll-container').forEach(c => { const words = c.querySelectorAll('.highlight-word'); … }) : un écouteur par bloc, chacun ne comptant que ses mots — c'est ainsi que les scènes 2 et 3 de cette page sont instanciées.