Text Reveal on Scroll — Free HTML CSS JS Word Snippet

Text Reveal on Scroll · Scroll · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Per-word spans
Each word brightens independently.
Sticky pinned text
Holds still while it reveals.
Progress-mapped reveal
Lit count tracks scroll progress.
Responsive lead
Words light slightly ahead of progress.
Fully reversible
Scrolling up dims words again.
rAF-throttled scroll
Passive, one update per frame.
Cheap class toggles
Only changed words restyle.
Wrap-preserving
Natural line breaks kept.

About this UI Snippet

Text Reveal on Scroll — Words Brighten as You Scroll

Screenshot of the Text Reveal on Scroll snippet rendered live

Text reveal on scroll is the editorial effect where a paragraph sits dim and, as you scroll, its words light up from gray to white one after another — so reading and scrolling become the same motion. This snippet builds it with plain HTML, CSS, and a compact vanilla JavaScript scroll handler tied to a pinned section.

Splitting the paragraph into words

On load, JavaScript splits the paragraph on whitespace and wraps each word in a .tr-word span, starting them all in a muted gray. Wrapping words individually is what lets them brighten one at a time — you cannot transition part of a text node — and rejoining with spaces preserves natural wrapping. Each word transitions its color over a quarter second, so lighting one is a smooth fade rather than a snap.

Pinning the text while it reveals

The section is taller than the viewport (160vh) and the paragraph is position: sticky, so it pins near the middle of the screen and stays there while you scroll through the section's extra height. That pinned window is what gives the reveal room to happen: the text holds still and the words light up as you scroll past, rather than the text scrolling away mid-reveal. This sticky-in-a-tall-section pattern is the backbone of most scroll-linked effects.

Mapping scroll to lit words

Each frame, update() computes a 0–1 progress from how far the section has scrolled through its range (-sectionTop / (height - viewport)). It then lights the first progress * wordCount words by toggling the .lit class. A small lead factor (* 1.08) lights words slightly ahead of the exact progress point, which makes the reveal feel responsive and ensures the last words finish lighting before you scroll out. Because it is a class toggle driven by progress, scrolling back up dims the words again — the effect is fully reversible.

Efficient scroll handling

The scroll listener is { passive: true } and throttled with requestAnimationFrame behind a ticking flag, so the measurement and the class updates run at most once per frame no matter how many scroll events fire. A resize listener re-runs it since the progress math depends on viewport height. Toggling classes is cheap, and only the words whose state actually changes trigger a style recalc.

Why progress-based, not per-word observers

You could use an IntersectionObserver per word, but tying the whole paragraph to a single scroll-progress value is simpler and gives precise, reversible control — you always know exactly how many words should be lit for any scroll position, and there is one calculation instead of dozens of observer callbacks.

Customizing it

Change the dim and lit colors (or fade other properties like opacity or blur), adjust the section height to slow or speed the reveal, tune the lead factor, or reveal by letter instead of word. Pair it with a text generate headline above or stacking scroll cards below for a scroll-driven story.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA dim paragraph pins in the middle of a tall section.
  2. 2
    Scroll downWords brighten from gray to white one after another.
  3. 3
    Scroll back upWords dim again — the reveal is reversible.
  4. 4
    Adjust the paceChange the section height to slow or speed it.
  5. 5
    Recolor the revealEdit the dim and lit word colors.
  6. 6
    Swap the propertyFade opacity or blur instead of color.

Real-world uses

Common Use Cases

Mission statements
Reveal copy above stacking scroll cards.
Manifesto sections
Pair with a text generate headline.
About pages
Tell a story as the reader scrolls.
Product narratives
Long-form intros
Set the tone before the content.
Scroll-link demos
A reference for progress-based word reveals.

Got questions?

Frequently Asked Questions

Each word brightens on its own, and you cannot transition part of a text node, so JavaScript wraps every word in a span starting in muted gray. Lighting a word toggles a class that transitions its color. Rejoining the spans with spaces preserves the natural line wrapping of the paragraph.

The section is 160vh tall and the paragraph is position: sticky, so it pins near the middle of the screen and stays there while you scroll through the section's extra height. That gives the reveal room to happen on still text, rather than the paragraph scrolling away mid-reveal. It is the standard sticky-in-a-tall-section pattern for scroll effects.

Each frame, update computes a 0–1 progress from how far the section has scrolled through its range, then lights the first progress times word-count words by toggling a lit class. A small lead factor lights words slightly ahead of exact progress so it feels responsive and the last words finish before you scroll out. Scrolling up reverses it.

Tying the whole paragraph to one scroll-progress value is simpler and gives precise, reversible control — you always know exactly how many words should be lit at any scroll position, with one calculation instead of dozens of observer callbacks. It also makes the lead factor and easing trivial to tune in one place.

Render the paragraph as an array of word spans and keep a lit-count in state, updating it from a scroll handler set up in a mount effect with cleanup. Drive each word class from its index versus the lit count. Keep the ticking flag in a ref. The sticky CSS ports directly; in Tailwind use sticky positioning and toggle text color utilities per word.