A reversible, scroll-linked reading front for editorial copy. SplitText breaks a statement into words; unread words stay ghosted, the active word lifts through a brief orange-to-lime flash, and completed words settle to calm white. Optional progress and coordinate elements can follow the same scrubbed ScrollTrigger.
Quick Start
1. Add to your HTML <head>:
<link rel="stylesheet" href="path/to/style.css">
2. Add the statement to your <body>:
<p class="scroll-highlight">
Attention is chosen. Read deliberately, one word at a time.
</p>
3. Add before the closing </body> tag:
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/ScrollTrigger.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/SplitText.min.js"></script>
<script src="path/to/script.js"></script>
GSAP and SplitText are free. Lenis is optional; load https://unpkg.com/lenis@1.3.17/dist/lenis.min.js before script.js for the smooth-scroll integration used by the demo.
Options
Set these data attributes on .scroll-highlight. The original data-attribute API remains supported.
| Attribute | Values | Default | Description |
|---|---|---|---|
data-highlight-dim |
0 to 1 |
0.16 |
Opacity of unread words |
data-highlight-accent |
true, false |
true |
Enables the orange/lime active-word front before white settle |
data-highlight-lift |
Number in px | 7 |
Distance the active word lifts |
data-highlight-scrub |
true or seconds |
true |
Direct scrub, or a numeric catch-up duration |
data-highlight-trigger |
CSS selector | Current block | Uses another element as the scroll range; ideal for a sticky stage |
data-highlight-progress |
CSS selector | None | Element whose horizontal scale displays reading progress |
data-highlight-current |
CSS selector | None | Element updated with the completed/total word coordinate |
Set the two front colours with CSS custom properties:
.scroll-highlight {
--accent: #c8ff00;
--highlight-edge: #ff6b35;
}
Examples
Standalone editorial copy
Add to your HTML <body>:
<p class="scroll-highlight" data-highlight-dim="0.1" data-highlight-lift="5">
Some ideas only arrive when every word receives its proper time.
</p>
The block itself supplies the trigger range, matching the original drop-in behaviour.
Sticky stage with a progress rail
Add to your HTML <body>:
<section class="reading-sequence" data-reading-sequence>
<div class="reading-stage">
<p class="scroll-highlight"
data-highlight-trigger="[data-reading-sequence]"
data-highlight-progress="[data-reading-progress]"
data-highlight-current="[data-reading-current]">
Attention is chosen. Read deliberately, one word at a time.
</p>
<span class="progress-fill" data-reading-progress></span>
<span data-reading-current>10 / 10</span>
</div>
</section>
The external trigger uses top top to bottom bottom, allowing the text stage to remain sticky while one timeline drives words, rail, and coordinate.
White-only reading front
Add to your HTML <body>:
<p class="scroll-highlight" data-highlight-accent="false" data-highlight-scrub="0.5">
The same reversible opacity and lift, without an accent flash.
</p>
CSS Classes
| Class | Description |
|---|---|
.scroll-highlight |
Reusable block selected by the script |
.sh-word |
Word span generated by SplitText |
.is-reading |
Added after a block has been split and initialised |
Accessibility
- Reduced-motion readers receive the complete statement at full opacity with no split animation or extended scroll runway.
- Without JavaScript, or when a CDN is blocked, the original unsplit statement and complete progress rail remain visible.
- The effect follows native scroll position, so keyboard, wheel, trackpad, and touch scrolling all operate it.
- Text remains real, selectable text; SplitText reverts its generated spans during cleanup.
Performance and Cleanup
Each block uses one SplitText instance, one timeline, and one ScrollTrigger. Initialisation waits for document.fonts.ready so line wrapping is measured against the loaded display font. Cleanup kills ScrollTriggers, reverts every split, removes the GSAP ticker callback, destroys Lenis, and removes the named Lenis refresh listener.
Dependencies
Required:
- GSAP 3.12+
- ScrollTrigger
- SplitText
Optional:
- Lenis 1.3+