Scroll Highlighter
v1.0.1FreeScroll
A premium typography scroll-highlighter that highlights text word-by-word with solid, wavy, or dotted styles based on page scroll or custom motion values.
Scroll Highlighter
PointerScrolla11y
Installation
Step 1. Initialize ProjectOne-time setup
Run once in your project root to configure paths and dependencies:
npx vectorvesper initStep 2. Add ComponentInstalls source & assets
Downloads and registers the component in your project:
npx vectorvesper add scroll-highlighterStep 3. Required DependenciesAuto-installed with CLI
If installing manually or managing your package lock:
npm install framer-motionPackages:framer-motion
Usage
Import
Usage
This component respects
prefers-reduced-motion and provides a graceful fallback.Props
A paragraph that highlights itself word by word as it scrolls into view, like someone running a marker across the line. The mark can be a solid block, a hand-drawn wave, or a dotted underline.
<ScrollHighlighter />
The passage and its highlight.
| Prop | Type | Default | Description |
|---|---|---|---|
text* | string | — | The passage. Required. |
variant | "solid" | "wavy" | "dotted" | "solid" | What the mark looks like. It also sets the three values below. |
highlightColor | string | per variant | Colour of the mark. Solid uses a translucent yellow; wavy and dotted use a solid one, since a thin line needs the weight. |
highlightHeight | number | 0.55 solid · 0.28 wavy · 0.18 dotted | Thickness of the mark, as a fraction of the line height. |
highlightY | number | 0.38 solid · 0.68 wavy · 0.82 dotted | Where the mark sits vertically, 0 at the top of the line and 1 at the bottom. Solid sits across the text; the other two sit under it. |
highlightOpacity | number | 1.0 | Opacity of the mark. |
gradientColors | [string, string] | — | Runs the mark as a gradient between two colours instead of a flat fill. |
overlap | number | 1.5 | How far the mark extends past each word, so consecutive words join into a continuous stroke. |
hoverGlow | boolean | true | Lifts a word when the cursor is over it. |
textColor | string | "#646973" | Colour of words not yet reached. |
activeTextColor | string | "#FFFFFF" | Colour of words the highlight has passed. |
baseFontOpacity | number | 1.0 | Opacity of words not yet reached. Lower it to fade them back further. |
activeFont | "default" | "dramatic" | "default" | Whether highlighted words switch to a second face as they activate. `dramatic` is the built-in serif italic. |
baseFontClass | string | "" | Class applied to words not yet reached. |
dramaticFontClass | string | "font-cormorant italic" | Class applied to activated words when activeFont is `dramatic`. The default names a font the component does not load — point it at one of yours. |
sticky | boolean | false | Pins the passage while the highlight runs, instead of letting it scroll past. |
fullscreen | boolean | false | Gives the passage the full viewport height. |
scrollSpeed | number | 0.001 | How quickly the highlight advances relative to the scroll. |
offset | [string, string] | ["start 0.85", "end 0.35"] | Where in the viewport the highlight starts and finishes. The defaults begin just as the passage enters and end before it leaves. |
customProgress | MotionValue<number> | — | Drives the highlight from your own value instead of the scroll position — for a timeline, or a shared scroll container. |
smooth | boolean | true | Eases the highlight rather than tracking the scrollbar exactly. |
physicsProfile | PhysicsProfileName | "gentle" | Named spring for that easing. Four are provided. |
springConfig | SpringConfig | from the profile | Your own damping, stiffness and mass, overriding the profile entirely. |
className | string | "" | Applied to the wrapper. |
Presets
| Preset | Description | Key Overrides |
|---|---|---|
"gentle" | The default. Settles without overshoot. | damping=25stiffness=140mass=0.6 |
"buttery" | Slower and heavier, with a long glide. | damping=35stiffness=100mass=1.0 |
"snappy" | Quick and tight. | damping=22stiffness=200mass=0.5 |
"bouncy" | Overshoots and settles back. | damping=12stiffness=90mass=0.8 |
Examples
Basic passage
import ScrollHighlighter from "@/components/vv/scroll-highlighter/ScrollHighlighter";
<ScrollHighlighter text="We build interfaces that feel like objects rather than pages." />A hand-drawn underline, pinned
<ScrollHighlighter
text="Every detail earns its place."
variant="wavy"
highlightColor="#7EACB5"
sticky
fullscreen
physicsProfile="buttery"
/>Driven by your own progress
const progress = useMotionValue(0);
<ScrollHighlighter text={copy} customProgress={progress} />The highlight is scroll-driven, so it only moves when the visitor scrolls. Set smooth={false} when reduced motion is preferred and it tracks the scrollbar exactly, with no easing of its own.
The
dramatic font class names Cormorant, which the component does not load. Either load it or point dramaticFontClass at a face you already have.Every word is measured and animated separately, so cost scales with length. A long essay in one instance is heavier than several short passages.