Table of contents
- 01 — Fade reveal (the universal pattern)
- 02 — Parallax
- 03 — Animate any CSS property on scroll
- 04 — Sticky / pin sections
- 05 — Section snapping
- 06 — Text split + reveal
- 07 — Animated counters
- 08 — Video scrubbing
- 09 — Horizontal scroll sections
- 10 — CSS variable binding
- 11 — Scroll callbacks (onEnter / onLeave)
- 12 — Native CSS scroll-driven animations
01
Fade reveal — the universal pattern
The most common scroll animation: elements fade up into view as the user scrolls past them. Every major website uses this. AOS and ScrollReveal.js popularised it. svg-scroll-draw does it in one call with scrollReveal.
import { scrollReveal } from 'svg-scroll-draw/reveal';
// Default: fade up (opacity 0→1, translateY 32→0)
scrollReveal('.card');
// Custom from state
scrollReveal('.feature', {
from: { opacity: 0, y: 40, scale: 0.96 },
stagger: 0.1,
easing: 'ease-out',
once: true,
});
// Named presets: fadeUp|fadeDown|fadeLeft|fadeRight|scale|flip|flipX
scrollReveal('.badge', { preset: 'scale' });02
Parallax
Move elements at a different rate than scroll. Background images, floating badges, decorative shapes. Negative speed = opposite direction.
import { scrollParallax } from 'svg-scroll-draw';
scrollParallax('#hero-bg', { speed: 0.4 }); // slower than scroll
scrollParallax('#floating-badge',{ speed: -0.2 }); // opposite direction
scrollParallax('#side-element', { speed: 0.3, axis: 'x' }); // horizontal03
Animate any CSS property on scroll
Not just opacity and transform — any CSS property can be driven by scroll position. Colors, backgrounds, borders, clip-paths, filter, font-size, anything.
import { scrollAnimate } from 'svg-scroll-draw';
// Color transition (section background shifts as you scroll through)
scrollAnimate('#section', {
props: {
backgroundColor: ['#ffffff', '#0d0d0d'],
color: ['#000000', '#ffffff'],
},
});
// Border radius morph
scrollAnimate('#card', {
props: { borderRadius: ['0px', '24px'] },
});
// Blur reveal
scrollAnimate('#image', {
props: { filter: ['blur(20px)', 'blur(0px)'], opacity: [0.3, 1] },
easing: 'ease-out',
once: true,
});04
Sticky / pin sections
The Apple product page pattern. An element stays fixed while content scrolls past it. Use scrollPin— it wraps the target in a spacer so the page layout doesn't jump.
import { scrollPin } from 'svg-scroll-draw/pin';
// Pin product image while feature text scrolls
scrollPin('#product-image', {
top: 80, // 80px from viewport top (below nav)
pinDistance: window.innerHeight * 3, // stay pinned for 3 viewport heights
onEnter: () => image.classList.add('active'),
onLeave: () => image.classList.remove('active'),
onEnterBack: () => image.classList.add('active'),
onLeaveBack: () => image.classList.remove('active'),
});05
Section snapping
Snap the viewport to the nearest section when the user stops scrolling. Custom easing, configurable threshold, programmatic control.
import { scrollSnap } from 'svg-scroll-draw/snap';
const snap = scrollSnap('.section', {
duration: 600,
easing: 'ease-in-out',
threshold: 0.3, // snap if scrolled 30%+ past a section
onSnap: (index) => updateNav(index),
});
// Programmatic
snap.snapTo(2); // smooth scroll to section 2
snap.getCurrentIndex(); // → active section index
snap.destroy();06
Text split + reveal
Split text into chars, words, or lines and stagger-animate each piece. Free replacement for GSAP SplitText (which requires a paid Club GreenSock subscription).
import { scrollText } from 'svg-scroll-draw/text';
// Word-by-word headline reveal
scrollText('#headline', {
split: 'words',
stagger: 0.07,
from: { opacity: 0, y: 32 },
easing: 'ease-out',
once: true,
});
// Typewriter (char by char)
scrollText('#subtitle', {
split: 'chars',
stagger: 0.015,
from: { opacity: 0 },
easing: 'linear',
once: true,
});07
Animated counters
Numbers that count up as they scroll into view. Stats sections, pricing, social proof.
import { scrollCounter } from 'svg-scroll-draw';
scrollCounter('#users', { to: 50000, format: n => Math.round(n).toLocaleString() + '+' });
scrollCounter('#revenue', { to: 1250000, format: n => '$' + Math.round(n).toLocaleString() });
scrollCounter('#nps', { to: 94.7, decimals: 1, format: n => n.toFixed(1) + '%' });
scrollCounter('#bundle', { to: 9, format: n => '~' + Math.round(n) + ' KB' });08
Video scrubbing
Tie <video>.currentTime to scroll position. The Apple / Stripe product video pattern — free, no GSAP needed.
import { scrollVideo } from 'svg-scroll-draw/video';
scrollVideo('#hero-video', {
trigger: { start: 'top top', end: 'bottom top' },
});09
Horizontal scroll sections
Vertical scroll drives horizontal movement. Set up sticky CSS, one call drives the transform.
import { scrollHorizontal } from 'svg-scroll-draw/horizontal';
// CSS: .outer{height:400vh} .sticky{position:sticky;top:0;height:100vh;overflow:hidden}
// .track{display:flex;width:max-content}
scrollHorizontal('.track', {
distance: document.querySelector('.track').scrollWidth - window.innerWidth,
easing: 'linear',
});10
CSS variable binding
Expose scroll progress as a CSS custom property. Then CSS calc() drives everything — color, size, position, filter — with zero extra JS.
import { scrollProgress } from 'svg-scroll-draw/progress';
scrollProgress('#section', { easing: 'ease-in-out' });#section {
/* Drive any CSS property from the variable */
opacity: calc(var(--scroll-progress-eased));
transform: translateY(calc((1 - var(--scroll-progress-eased)) * 40px));
background: hsl(
calc(240 + var(--scroll-progress) * 120),
60%, 10%
);
}11
Scroll callbacks (onEnter / onLeave)
Fire code when scroll crosses the trigger zone — in either direction. Nav highlighting, lazy loading, analytics events, state updates.
import { scrollAnimate } from 'svg-scroll-draw';
scrollAnimate('#section', {
props: { opacity: [0.4, 1] },
trigger: { start: 'top center', end: 'bottom center' },
onEnter: () => nav.setActive('section'),
onLeave: () => nav.clearActive('section'),
onEnterBack: () => nav.setActive('section'),
onLeaveBack: () => nav.clearActive('section'),
});12
Native CSS scroll-driven animations
When animation-timeline: view() is supported (Chrome 115+, Edge 115+), svg-scroll-draw automatically uses it — zero JS scroll listeners, zero rAF, pure compositor animation. Falls back silently in older browsers.
import { scrollAnimate } from 'svg-scroll-draw';
// native: true is the default — uses CSS fast path when eligible
scrollAnimate('#hero', {
props: { opacity: [0, 1], transform: ['translateY(32px)', 'translateY(0)'] },
// native: true (default) — runs on compositor when supported
});
// Force JS engine (needed for callbacks, velocity, custom easing fns)
scrollAnimate('#section', {
props: { opacity: [0, 1] },
native: false,
onEnter: () => activate(),
});One library, all patterns.
scrollRevealscrollAnimatescrollParallaxscrollPinscrollSnapscrollTextscrollCounterscrollVideoscrollHorizontalscrollProgressscrollDrawdevtools