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

vercel-snap-text.tsx

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:

  1. Scroll driver — an invisible overflow-y: scroll overlay with scroll-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.

  2. 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

PropTypeDefault
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 mandatory with 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.8 by default). Increase stiffness for snappier transitions, increase damping to reduce bounce.
  • itemHeight defines 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, minimum 0.15).
  • The prefix text 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 ResizeObserver keeps sectionHeight in sync with the container — no stale measurements after window resize.