Vercel Snap Text
A scroll-driven text list that snaps exactly to each item. The active item scales up with an optional prefix, others fade and indent — all animated with Motion springs.
Installation
File Structure
Usage
import { VercelSnapText } from "@/components/unlumen-ui/vercel-snap-text";
<VercelSnapText
items={["the platform", "the brand", "the future"]}
prefix="We Design"
className="w-full h-screen"
/>;How it works
The component renders two layers inside a single container:
-
Scroll driver — an invisible
overflow-y: scrolloverlay withscroll-snap-type: y mandatory. Each item gets a full-height snap section so the browser always stops exactly on an item boundary. No JavaScript rounding required. -
Visual layer — a spring-animated track that moves to center the active item. Each row animates its horizontal offset (Motion spring), font size, and opacity based on its distance from the current scroll position.
API Reference
VercelSnapText
| Prop | Type | Default |
|---|---|---|
items | string[] | - |
prefix? | string | "" |
itemHeight? | number | 96 |
stiffness? | number | 200 |
damping? | number | 28 |
className? | string | - |
Credits
Inspired by the scroll-snap text effect seen on vercel.com. Thanks to the Vercel design team for the original idea.
Notes
- The component requires a fixed height on the root (e.g.
h-screen,h-[600px]). Without it, the scroll driver has no height and snapping won't work. - The invisible scroll overlay uses
scroll-snap-type: y mandatorywith each item as a full-height snap section. The browser handles snapping natively — no JS rounding needed. - The track animation uses a Motion spring (
stiffness: 280,damping: 30,mass: 0.8by default). Increasestiffnessfor snappier transitions, increasedampingto reduce bounce. itemHeightdefines the height of each row in the visual layer. All items share this height — it's the snap unit. Adjust it based on your font size.- Non-active items fade out proportionally to their distance from the current index (
opacity = 1 - dist × 0.82, minimum0.15). - The
prefixtext is always visible and doesn't animate. Only the item list scrolls. - Font size uses
clamp(1.75rem, 4vw, 4.5rem)— it scales responsively. Override it via className if you need a different size. - Scrollbar is hidden via
scrollbarWidth: "none"and-ms-overflow-style: none. The scroll area is fully invisible. - A
ResizeObserverkeepssectionHeightin sync with the container — no stale measurements after window resize.