Text Highlight Scroll
Trente-trois mots à 15 % d'opacité dans une zone de 280 px qui défile sur 620 px ; à chaque événement scroll du conteneur, l'écouteur livré allume floor(ratio × 33) mots en #e0e7ff, passe le suivant en violet et fait suivre une barre de 3 px — texte et jauge tiennent dans un bloc sticky pour rester sous les yeux jusqu'au dernier mot.
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



Comment ça marche
Le HTML vendu empile .demo-preview (fond #0a0a0f, texte blanc, sans-serif, position: relative : le bloc porte son propre fond et sa propre police, il reste lisible collé dans une page blanche) → .sdc-scroll-container.ss-highlight-scroll (width: 100%, height: 280px, overflow-y: auto, scroll-behavior: smooth, barre de défilement WebKit de 4 px) → .ss-highlight-content (height: 900px) → .ss-highlight-pin (position: sticky, top: 0, padding: 40px 16px) → .ss-highlight-text#ssHighlightText (line-height: 2) contenant 33 span.ss-highlight-word, puis une piste .ss-highlight-progress-track de 3 px (rgba(255,255,255,.1), margin-top: 16px) et son remplissage #ssHighlightFill (width: 0, dégradé #8b5cf6 → #d946ef, transition: width .1s). Chaque mot est un inline-block de 1rem, font-weight: 600, en rgba(255,255,255,.15) avec transition: color .2s ; deux classes d'état : .lit (#e0e7ff) et .accent (#a78bfa). Sous la zone, .sdc-scroll-hint « Scrollez ↓ » (.65rem, opacité qui pulse de .3 à .7 en 2 s, sdcHintPulse). Mesuré sur une page vierge de 800 px de large : le paragraphe occupe 3 lignes de 32 px, de 40 à 136 px du haut de la zone ; la piste finit à 155 px ; le bloc épinglé fait 195 px ; les 705 px restants du contenu sont vides et ne servent qu'à donner 620 px de course (900 − 280). À 1 280 px, 2 lignes (64 px) et un bloc de 163 px.
L'écouteur est une IIFE en ES5 (var, function, aucune variable globale) qui parcourt document.querySelectorAll('.ss-highlight-scroll') : chaque zone trouve son texte et son remplissage à l'intérieur d'elle-même (container.querySelector), si bien que plusieurs zones dans une même page sont animées indépendamment. Les mots sont les span du HTML — le script ne reconstruit rien par innerHTML, donc les espaces entre balises sont conservés (écart mesuré entre deux mots : 4,45 px à 1rem) et l'on change le texte en éditant les spans. À chaque événement scroll du conteneur (écouteur passive), et au resize : ratio = scrollTop / (scrollHeight − clientHeight), litCount = Math.floor(ratio × 33) ; les mots d'indice i < litCount reçoivent .lit, celui d'indice i === litCount reçoit .accent (le mot « en cours de lecture », en violet), et fill.style.width = ratio × 100 + '%'. Mesuré : à 100 px de défilement, 5 mots allumés et le 6ᵉ en violet, barre à 16,1 % ; à 300 px, 15 ; à 600 px, 31 ; à 620 px, les 33 en #e0e7ff, plus aucun accent, barre à 100 %. Soit un mot tous les 18,8 px de défilement. La fonction est appelée une fois à l'initialisation : avant tout défilement, aucun mot n'est allumé mais le premier porte déjà l'accent et la barre est à 0 %.
Pourquoi le bloc est épinglé. Le paragraphe fait 96 px de haut dans une fenêtre de 280 px pour 620 px de course : s'il défilait avec le contenu, il sortirait entièrement du cadre à scrollTop = 136 (ratio 0,219, 7 mots sur 33) et les 26 mots suivants s'allumeraient hors champ. .ss-highlight-pin en position: sticky; top: 0 dans .ss-highlight-content règle cela : mesuré, le texte reste de 40 à 136 px du haut de la zone à toutes les positions de 0 à 620 px, la piste à 155 px. Un élément sticky ne quitte jamais son parent : le bloc de 195 px peut glisser jusqu'à 900 − 195 = 705 px, au-delà des 620 px de course, il ne se décroche donc jamais. À 390 px de large, le paragraphe passe à 6 lignes (192 px) et le bloc à 291 px, plus haut que la fenêtre : en fin de course il est repoussé de 11 px vers le haut (texte de 29 à 221 px), toujours entièrement visible. Détail : le repère « Scrollez ↓ » est positionné par rapport à .demo-preview (bottom: 8px) et chevauche les 10 derniers pixels de la zone.
Accessibilité
- prefers-reduced-motion pris en charge dans le CSS :
@media (prefers-reduced-motion: reduce) { .sdc-scroll-hint { animation: none } }arrête la pulsation du repère « Scrollez ↓ » (sdcHintPulse, opacité .3 ↔ .7 toutes les 2 s, infinie), seule animation autonome de l'effet. Le surlignage est piloté par le défilement de l'utilisateur, donc sans risque vestibulaire ; les transitions de couleur (.2 s) et de largeur (.1 s) restent actives. - Contraste mesuré sur
#0a0a0f: mots non lusrgba(255,255,255,.15)=#2f2f33, 1,48:1 — illisibles par construction tant que l'on n'a pas défilé ; mots lus#e0e7ff16,0:1 ; mot courant#a78bfa7,3:1 ; barre 4,7 à 5,7:1 ; repère « Scrollez ↓ » 2,6:1 à 10,4 px. Si la phrase porte une information (un manifeste, un programme), montez l'opacité de base à.45(4,5:1) : l'effet reste une mise en valeur progressive, il cesse d'être une révélation. - Lecteurs d'écran : les 33 spans sont du texte en ligne ordinaire, lu en une phrase quelle que soit la position de défilement — ne posez pas
aria-hiddendessus. Posez-le en revanche sur le repère « Scrollez ↓ » et sur la piste de progression (undivsans rôle), ou donnez à celle-cirole="progressbar"avecaria-valuenowmis à jour dans la fonctionupdatede l'écouteur. - Clavier : la zone
overflow-y: auton'a pas detabindex. Chrome 130+ rend focalisables les zones défilantes sans contenu focalisable, pas tous les navigateurs : ajouteztabindex="0"et unaria-labelau conteneur pour que les flèches et Espace le fassent défiler. Tactile : le défilement à un doigt d'une zoneoverflow-y: autoimbriquée fonctionne sur iOS et Android et déclenche bien les événementsscroll; en bout de course, molette et doigt passent à la page (aucunoverscroll-behaviorn'est posé).
Compatibilité navigateur
ES5 (var, function, Array.prototype.forEach.call sur la NodeList), classList, événement scroll sur un conteneur overflow-y: auto ; côté CSS, position: sticky, transition, linear-gradient, scroll-behavior (facultatif) et ::-webkit-scrollbar (WebKit/Blink seulement, Firefox garde sa barre native). Aucun animation-timeline : contrairement aux 45 effets de cette catégorie animés par timeline CSS (fx-0545, fx-0546, fx-0592…), que Firefox ne lit dans aucune version, ce surlignage fonctionne sur Firefox. Aucune dépendance : pas de bibliothèque, pas d'image, pas de fichier hébergé — les trois blocs collés suffisent.
Sans JavaScript : un paragraphe à 15 % d'opacité, épinglé, dans une boîte de 280 px qui défile à vide, une barre vide, un repère qui pulse. Sans position: sticky, le bloc défile avec le contenu et quitte le cadre à 136 px alors que les mots continuent de s'allumer. Sans scroll-behavior, le défilement programmé est instantané ; sans ::-webkit-scrollbar, la barre est celle du système.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
<div class="sdc-scroll-container ss-highlight-scroll">
<div class="ss-highlight-content">
<div class="ss-highlight-pin">
<div class="ss-highlight-text" id="ssHighlightText">
<span class="ss-highlight-word">Le </span>
<span class="ss-highlight-word">scrollytelling </span>
<span class="ss-highlight-word">transforme </span>
<span class="ss-highlight-word">… </span>
</div>
<div class="ss-highlight-progress-track">
<div class="ss-highlight-progress-fill" id="ssHighlightFill"></div>
</div>
</div>
</div>
</div>
<span class="sdc-scroll-hint">Scrollez ↓</span>
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éfaut | Effet |
|---|---|---|
| Texte (HTML — les span.ss-highlight-word) | 33 mots, un span par mot | Le script compte les spans présents dans .ss-highlight-text à l'initialisation : 12 mots ou 80, le rythme s'adapte (course ÷ nombre de mots). Gardez l'espace final à l'intérieur de chaque span ou un retour à la ligne entre eux : avec inline-block, c'est ce qui sépare les mots (4,45 px à 1rem). Plusieurs zones .ss-highlight-scroll dans une même page sont chacune animées par la même IIFE. |
| Bloc épinglé (CSS — .ss-highlight-pin position / padding) | sticky ; top: 0 ; padding 40px 16px | Garde texte et piste dans le cadre pendant toute la course : mesuré, le paragraphe reste de 40 à 136 px du haut de la zone de 0 à 620 px de défilement. position: static rend le comportement d'un paragraphe qui défile avec son contenu (il quitte le cadre à 136 px alors que 7 mots sont allumés). Le padding fixe la position du texte dans la fenêtre. |
| Hauteur du contenu (CSS — .ss-highlight-content height) | 900px (course de 620 px) | La course = hauteur − 280 px. Elle fixe le rythme : 620 / 33 = 18,8 px par mot. 500px (220 px de course, 6,7 px par mot) pour une lecture en un coup de molette ; 1400px pour une lecture lente. Le bloc épinglé (195 px à 800 px de large) reste accroché tant qu'il est moins haut que la fenêtre de 280 px, quelle que soit la hauteur de contenu. |
| Hauteur de la zone (CSS — .sdc-scroll-container height) | 280px | Fenêtre visible. Plus haute, la course diminue à contenu égal (900 − hauteur). Le texte étant épinglé, la hauteur n'a qu'à contenir le paragraphe et la piste : 195 px à 800 px de large, 291 px à 390 px (au-delà de la fenêtre, le bloc est repoussé de la différence en fin de course — 11 px, sans cacher le texte). |
| Couleurs des mots (CSS — .ss-highlight-word, .lit, .accent) | rgba(255,255,255,.15) / #e0e7ff / #a78bfa | Trois états. L'opacité de base règle la lisibilité avant lecture (1,48:1 à .15, 4,5:1 à .45). L'accent n'est porté que par un mot à la fois — le premier dès le chargement ; après le dernier mot, plus aucun accent. |
| Transition de couleur (CSS — .ss-highlight-word transition) | color .2s | Fondu de chaque mot vers .lit / .accent. À 0, l'allumage suit le défilement image par image ; à .5s, une traînée : plusieurs mots en cours de fondu à la fois après un coup de molette. |
| Typographie (CSS — .ss-highlight-word font-size / font-weight, .ss-highlight-text line-height) | 1rem / 600 / 2 | inline-block sur chaque mot : les mots ne se coupent jamais en fin de ligne et les espaces viennent du HTML entre les spans (4,45 px à 1rem), que le script conserve puisqu'il ne réécrit pas le HTML. Un line-height de 1,5 à 1,6 suffit en grand corps. |
| Barre de progression (CSS — .ss-highlight-progress-fill) | dégradé #8b5cf6 → #d946ef, 3 px, transition width .1s | Largeur écrite en % par l'écouteur. Supprimez piste et remplissage si vous n'en voulez pas : l'écouteur teste if (fillEl) avant d'écrire. |
| Défilement lissé (CSS — .sdc-scroll-container scroll-behavior) | smooth | S'applique aux sauts programmés (scrollTo, ancres) : les mots s'allument alors un à un pendant le glissement. Passez à auto si vous pilotez scrollTop vous-même image par image. |
FAQ
.ss-highlight-scroll déjà présentes : placez-le après le HTML (fin de body), ou chargez-le avec defer s'il vit dans le head. Les mots doivent être des span.ss-highlight-word dans .ss-highlight-text : c'est ce que le script compte. Et la zone doit réellement défiler — le contenu de 900 px dans une fenêtre de 280 px ; si vous avez réduit .ss-highlight-content sous la hauteur de la zone, il n'y a plus de course et le ratio reste à 0. Mesuré sur une page vierge avec les trois blocs collés tels quels : à 100 px de molette, 5 mots allumés et le 6ᵉ en violet, aucune erreur console..ss-highlight-pin, un bloc position: sticky; top: 0 dans un contenu de 900 px. La zone défile (la barre de défilement et la jauge le montrent), le paragraphe reste de 40 à 136 px du haut et les mots s'allument sous les yeux — sans cela, un paragraphe de 96 px sort d'une fenêtre de 280 px à 136 px de défilement, soit 7 mots allumés sur 33, et les 26 suivants s'allumeraient hors champ. Pour un texte qui défile avec son contenu, mettez .ss-highlight-pin { position: static } et écrivez un paragraphe assez long pour remplir la course, ou ramenez la hauteur de contenu à ce que le texte occupe.floor(ratio × n) dans un conteneur de 280 px, avec quatre différences. fx-0579 cible un seul conteneur par id en ES2015 (fonctions fléchées) et n'agit qu'au premier défilement ; fx-0591 est une IIFE ES5 qui anime toutes les zones .ss-highlight-scroll de la page et accentue le premier mot dès le chargement. fx-0591 ajoute la barre de progression et épingle le texte ; fx-0579 n'a ni jauge ni épinglage. fx-0579 pose display: inline (transition .3 s, lus en #fff, courant #a855f7) sur 64 mots ; fx-0591, inline-block, .2 s, #e0e7ff / #a78bfa sur 33 mots. Enfin le texte de fx-0579 remplit sa zone et défile avec elle ; celui de fx-0591 reste en place. Prenez fx-0579 pour un paragraphe long qui se lit en défilant ; fx-0591 pour une phrase courte qui reste sous les yeux, avec une jauge.