Lexicon / Scroll-driven

Scroll-driven

What scrolling plays · 13 named effects, each running below

  1. 01Scroll Progress Timeline
  2. 02View Progress Timeline
  3. 03Pinning & Scrub
  4. 04Parallax
  5. 05Horizontal Scroll Section
  6. 06Sticky Stacking Cards
  7. 07Scroll Velocity Skew
  8. 08Timeline Scope
  9. 09Path Drawing
  10. 10Word Scrub
  11. 11Curtain Overlap
  12. 12Depth of Field
  13. 13Scroll-Triggered Count
01 / Scroll-driven

Scroll Progress Timeline

How far you have scrolled is how far the animation has played. The hairline across the top of this page is one.

also called scroll-linked animation · page progress

The animation's progress is the scroll container's progress. Zero JavaScript, and it runs off the main thread — so it cannot jank even while the main thread is busy.

How it is builtanimation-timeline: scroll(root);
Open its page
scroll the page
0%
02 / Scroll-driven

View Progress Timeline

Every row times its own animation by its own trip across the screen, so no two are at the same point at the same moment.

also called view timeline · element-in-viewport progress

Each element measures its own passage through the viewport, so several different values exist at once. animation-range is the part most people miss — it decides which slice of that passage the animation occupies.

How it is builtview-timeline-name: --plate; animation-timeline: --plate; animation-range: entry 0% cover 45%;
Open its pageExhibit 04 · Urformen
scroll the page
03 / Scroll-driven

Pinning & Scrub

The picture holds still while the page keeps moving, and your scrolling becomes the playhead. Scroll back up and it plays backwards.

also called sticky section · pinned stage · ScrollTrigger pin + scrub

Pin holds the stage still while the page keeps scrolling. Scrub is the separate idea that progress follows scroll position rather than elapsed time, which is why it reverses exactly. Most “cinematic” sites are these two words.

How it is builtposition: sticky; top: 0; /* GSAP */ ScrollTrigger({ pin: true, scrub: 1 })
Open its pageExhibit 01 · Annie G.
scroll the page
Scrub to wipe
04 / Scroll-driven

Parallax

The layers move at different rates, and the difference between the rates is the part you read as distance.

also called differential scroll · multiplane

The oldest trick here and the easiest to overdo. It reads as depth only when the rates differ by a little; large offsets read as broken layout. Built on a view progress timeline it costs no JavaScript.

How it is built@keyframes drift { from { transform: translateY(-10%) } to { transform: translateY(10%) } } animation: drift linear both; animation-timeline: view(); /* each layer gets its own distance; the difference is the depth */
Open its page
scroll the page
Depth is a rate difference
05 / Scroll-driven

Horizontal Scroll Section

You scroll down; the content travels sideways. A list you ranked becomes a row you compare across.

also called pinned horizontal scroll · sideways gallery

Changing the axis changes what the content means: a stack implies rank, a track implies comparison. Travel must be measured from real content — a track that barely overflows reads as a glitch, not a journey.

How it is built/* --travel is the track's overflow, measured from its content */ transform: translateX(calc(-1 * var(--travel)));
Open its pageExhibit 02 · Night side
scroll the page
06 / Scroll-driven

Sticky Stacking Cards

Cards catch at the top one after another and pile over each other as you keep going.

also called card stack · sticky stack

One property on a list. Each item sticks at the same offset so later items slide over earlier ones and the stack builds itself. No library, no measurement, no scroll listener.

How it is builtli { position: sticky; top: 76px; }
Open its page
scroll the page
    07 / Scroll-driven

    Scroll Velocity Skew

    It reads how fast you are scrolling rather than where you have got to. Throw the page and the picture leans, then settles.

    also called skew on scroll · velocity distortion · momentum skew

    Velocity is a genuinely separate signal from position. The asymmetry is the craft: it rises about six times faster than it falls, which is what makes it read as momentum rather than twitch.

    How it is builtv += (target - v) * (target > v ? 0.34 : 0.055); transform: skewY(calc(var(--vel) * -4deg));
    Open its pageExhibit 03 · Florence
    throw the page

    velocity 0.00

    08 / Scroll-driven

    Timeline Scope

    A figure far down the panel drives the bar at the top of it. One element's passage moves something it sits nowhere near.

    also called timeline-scope · remote timeline · cross-tree driving

    A scroll timeline is normally visible only to the element's own descendants. timeline-scope lifts it onto a shared ancestor, so a bar at the top can be driven by a figure far below it — something that previously required JavaScript by definition.

    How it is builttimeline-scope: --plate; /* on a child */ view-timeline-name: --plate; /* on a sibling */ animation-timeline: --plate;
    Open its page
    scroll inside ↓
    bar driven by the figure below
    keep scrolling ↓
    — end —
    09 / Scroll-driven

    Path Drawing

    The line draws itself as you arrive, as though someone were tracing it in front of you.

    also called stroke-dashoffset scrub · line draw · SVG path reveal

    The robustness trick is pathLength="1": it normalises any path to a length of one, so the same two lines of CSS draw any shape without measuring it in JavaScript first.

    How it is builtpath { pathLength: 1; stroke-dasharray: 1; stroke-dashoffset: 1 } animation-timeline: view(); animation-range: entry 5% cover 50%;
    Open its page
    scroll the page

    Draws as you arrive

    10 / Scroll-driven

    Word Scrub

    The sentence lights up a word at a time, and how much of it has arrived is how far you have come down the page.

    also called read-along highlight · per-word reveal · text scrub

    Each word owns a different slice of one timeline, so the sentence resolves left to right as you descend. The overlap is the entire craft: too little and it strobes word by word, too much and the sentence arrives all at once.

    How it is built/* per word, i = its index */ animation-range: cover calc(var(--i) * 1.5%) cover calc(var(--i) * 1.5% + 26%);
    Open its showcase
    scroll the page
    11 / Scroll-driven

    Curtain Overlap

    The next panel covers the one before it rather than pushing it out of the way.

    also called panel slide-over · curtain reveal · section overlap

    Two rules and nothing else. The outgoing panel stays pinned while the incoming one rides over it, so the change reads as depth rather than travel. Note what is absent: no animation, no timeline, no measurement.

    How it is built.a { position: sticky; top: 0 } .b { position: relative; z-index: 2 }
    Open its page
    scroll the page
    01 — stays put
    02 — rides over
    12 / Scroll-driven

    Depth of Field

    Only the band across the middle of the screen is sharp. Everything above and below it falls out of focus.

    also called focus falloff · viewport-centre focus · scroll blur

    A camera metaphor applied to a list: attention is a narrow band, and everything outside it recedes. Keep the blur under about 6px — past that it stops reading as focus and starts reading as a rendering fault.

    How it is built@keyframes focusband { 0%, 100% { filter: blur(4.5px); opacity: .34 } 46%, 54% { filter: blur(0); opacity: 1 } }
    Open its page
    scroll the page
    13 / Scroll-driven

    Scroll-Triggered Count

    The figures wait until they are on screen, then run up to their value — once, not every time you pass.

    also called odometer · number roll · stat counter

    The tabular figures matter more than the easing — proportional numerals change width as they count and the whole row twitches. It fires once: a counter that replays on every pass is a tell that nobody checked it twice.

    How it is builtfont-variant-numeric: tabular-nums; new IntersectionObserver(fn, { threshold: 0.6 })
    Open its page
    plays once