Scroll✨ Premium

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.

JSTextHighlight

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

Citation phare d'un rapport de durabilité — manifeste d'une entreprise B Corp — Text Highlight Scroll exemple 1

① Citation phare d'un rapport de durabilité — manifeste d'une entreprise B Corp

QuandPage « Notre engagement » d'un rapport annuel généré statiquement (Hugo, export Next.js) : un bloc pleine largeur, la phrase du manifeste en 21 px, une jauge de lecture dessous.
PourquoiLa zone est autonome : elle défile dans sa propre boîte (overflow-y: auto), indépendamment du défilement de la page — un module de lecture que l'on pose au milieu d'un rapport long sans toucher au reste. L'écouteur livré (une IIFE ES5 d'une vingtaine de lignes) se colle en ligne dans le gabarit, sans bundler ni bibliothèque, et la barre de progression donne la part du manifeste lue. À 31 mots sur 620 px, un mot s'allume tous les 20 px, et le bloc épinglé garde la phrase sous les yeux jusqu'au dernier.
RéglagesMots à 21 px / 600, non lus rgba(238,247,240,.16), lus #eef7f0, courant #8ff0b4 (13:1 sur #0b1a14), barre en dégradé vert ; marges du bloc épinglé .ss-highlight-pin ramenées à 26px 32px 0 ; scroll-behavior: auto.
Charte de valeurs d'un cabinet d'architectes — page Philosophie — Text Highlight Scroll exemple 2

② Charte de valeurs d'un cabinet d'architectes — page Philosophie

QuandPage « Philosophie » d'un cabinet : trois principes en un mot (Rigueur. Sobriété. Durée.) puis une phrase, en serif de 27 px sur fond papier.
PourquoiMath.floor(ratio × n) donne à chaque mot sa propre tranche de défilement : 25 mots sur 620 px, un mot tous les 24,8 px — la lecture devient un geste, et les trois principes s'allument un par un, chacun en terre cuite le temps d'être « le mot courant ». Le fond clair montre que l'effet ne tient qu'à trois couleurs à changer ; le bloc (spans + CSS + écouteur) se colle dans un embed de CMS sans dépendre du reste de la page, et le script compte les spans qu'il trouve : 25 mots ou 33, rien à régler.
RéglagesGeorgia 27 px / 400, non lus rgba(28,25,20,.16), lus #1c1914, courant #8f3a1a (6,6:1 sur #f3efe7), barre #8f3a1a, repère et barre de défilement recolorés, marge haute du bloc épinglé à 30 px.
Programme d'un congrès médical — les thèmes de la journée — Text Highlight Scroll exemple 3

③ Programme d'un congrès médical — les thèmes de la journée

QuandPage programme d'un congrès publiée par le comité scientifique depuis du Markdown converti en HTML : en-tête dates et lieu, les thèmes du jour en 20 px, une jauge.
PourquoiUn seul fichier HTML suffit : les spans, la feuille de style et l'écouteur tiennent en ligne dans la page, sans fichier .js séparé à maintenir ni pipeline. Le mot courant en cyan (8,9:1 sur #071021) sert de curseur de lecture sur la liste « Oncologie de précision. Immunologie. Bioéthique. » ; le comité change les thèmes en éditant les spans, sans toucher au script, qui lit les mots dans le HTML.
RéglagesMots 20 px / 600, non lus rgba(230,241,255,.15), lus #e6f1ff, courant #38bdf8, barre #0ea5e9 → #38bdf8, 25 mots (un tous les 24,8 px), marges du bloc épinglé 26px 32px 0.

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 lus rgba(255,255,255,.15) = #2f2f33, 1,48:1 — illisibles par construction tant que l'on n'a pas défilé ; mots lus #e0e7ff 16,0:1 ; mot courant #a78bfa 7,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-hidden dessus. Posez-le en revanche sur le repère « Scrollez ↓ » et sur la piste de progression (un div sans rôle), ou donnez à celle-ci role="progressbar" avec aria-valuenow mis à jour dans la fonction update de l'écouteur.
  • Clavier : la zone overflow-y: auto n'a pas de tabindex. Chrome 130+ rend focalisables les zones défilantes sans contenu focalisable, pas tous les navigateurs : ajoutez tabindex="0" et un aria-label au conteneur pour que les flèches et Espace le fassent défiler. Tactile : le défilement à un doigt d'une zone overflow-y: auto imbriquée fonctionne sur iOS et Android et déclenche bien les événements scroll ; en bout de course, molette et doigt passent à la page (aucun overscroll-behavior n'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.

Chrome 56+✓ Complet (sticky sans préfixe)
Firefox 32+✓ Complet (barre de défilement native)
Safari 13+✓ Complet (6.1 à 12 avec -webkit-sticky)
Edge 16+✓ Complet
Mobile iOS 13+✓ Complet (défilement tactile de la zone)
Android Chrome✓ Complet (défilement tactile de la zone)

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) :

index.html — structure
<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>
🔒 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
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

Trois points. Le script est une IIFE qui s'exécute dès qu'elle est lue et cherche les zones .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.
Oui : texte et piste sont dans .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.
Même formule 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.