Scroll & Parallax
Scroll animations, parallax, scroll-driven CSS, scrollytelling, sticky and snap.
Updated
How do you animate elements on scroll?
Scroll animations reveal, move or transform content as the page scrolls. There are two approaches: IntersectionObserver in JavaScript, which adds a class when an element enters the viewport and works in every browser, and native CSS scroll-driven animations (animation-timeline: scroll() or view()), which tie an animation to the scroll position without JavaScript.
How to choose, performance and accessibility
- Use Fade Up or Stagger Cards for sections that appear, a reading Progress Bar on long articles, Parallax or sticky scrollytelling for storytelling pages, and Vertical Snap for full-screen slides.
- CSS scroll-driven animations run in Chrome, Edge and Safari 26 but not yet in Firefox, so keep content visible without them.
- Animate only
transformandopacity, and turn effects off forprefers-reduced-motion. - Fade Up, Progress Bar, Sticky Image Change and Vertical Snap are among the free effects with full code; the rest come with Premium.
Scroll Basics
20 fundamental scroll and parallax effects.
First element
Appears rising up with a fade
Second element
Progressive animation on scroll
Third element
Uses animation-timeline: view()
Fourth element
Each element animates individually
Fifth element
All CSS, zero JavaScript
Fade Up
Elements that appear rising up with a progressive fade via animation-timeline: view().
Effect page βView source code
<div class="demo-preview">
<div class="sdc-scroll-container">
<div class="sb-fadeup-content">
<div class="sb-fadeup-box"><h4>First element</h4><p>Appears rising up with a fade</p></div>
<div class="sb-fadeup-box"><h4>Second element</h4><p>Progressive animation on scroll</p></div>
<div class="sb-fadeup-box"><h4>Third element</h4><p>Uses animation-timeline: view()</p></div>
<div class="sb-fadeup-box"><h4>Fourth element</h4><p>Each element animates individually</p></div>
<div class="sb-fadeup-box"><h4>Fifth element</h4><p>All CSS, zero JavaScript</p></div>
</div>
</div>
<span class="sdc-scroll-hint">Scroll β</span>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 280px;
overflow: hidden;
position: relative;
background: rgb(10, 10, 15);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.sb-fadeup-content {
height: 900px;
padding: 60px 20px;
display: flex;
flex-direction: column;
gap: 24px;
}
.sb-fadeup-box {
padding: 24px;
border-radius: 14px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
text-align: center;
opacity: 0;
transform: translateY(60px);
animation: sbFadeUp linear both;
animation-timeline: view();
animation-range: entry 0% entry 50%;
}
.sb-fadeup-box h4 {
font-size: 0.9rem;
font-weight: 700;
color: #fff;
margin: 0 0 6px;
}
.sb-fadeup-box p {
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.5);
margin: 0;
}
.sdc-scroll-container {
width: 100%;
height: 280px;
overflow-y: auto;
position: relative;
scroll-behavior: smooth;
}
.sdc-scroll-container::-webkit-scrollbar {
width: 4px;
}
.sdc-scroll-container::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
}
.sdc-scroll-container::-webkit-scrollbar-thumb {
background: rgba(139, 92, 246, 0.4);
border-radius: 2px;
}
.sdc-scroll-hint {
position: absolute;
bottom: 8px;
left: 50%;
transform: translateX(-50%);
font-size: 0.65rem;
color: rgba(255, 255, 255, 0.3);
z-index: 5;
pointer-events: none;
animation: sdcHintPulse 2s ease-in-out infinite;
}
@keyframes sbFadeUp {
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes sdcHintPulse {
0%,
100% {
opacity: 0.3;
}
50% {
opacity: 0.7;
}
}/* Pure CSS β no JavaScript required */Scale In
Elements that scale up from a smaller size with animation-timeline: view().
Slide In
Elements sliding in from left and right alternately via view().
Parallax
Parallax effect where the background moves at a different speed via scroll(nearest).
Effect page βProgress Reveal
Text that progressively fills with an animated gradient via scroll(nearest) and @property.
Stagger Cards
Cards appearing in cascade with incremental animation-delay and view().
Horizontal Scroll
Horizontal scrolling with scroll-snap for smooth navigation between elements.
Effect page βBlur Reveal
Progressive focus effect with blur(20px) animated to blur(0) via view().
Counter Scroll
Animated counters that increment when they enter the container's view.
Scroll-Driven Fade
Native CSS animation driven by scroll with animation-timeline: view() and entry range.
View Timeline Scale
Elements that progressively scale up via animation-timeline: view() and cover range.
Scroll Progress Bar
Sticky progress bar linked to the container scroll via scroll(nearest).
Parallax Layers CSS
Layers moving at different speeds via scroll(nearest) for a depth effect.
Rotate On Scroll
Element performing a 720-degree rotation linked to scroll via scroll(nearest).
Slide In CSS Native
Native CSS left/right slide with animation-timeline: view() and cover range.
Blur to Clear
Transition from blur(10px) to blur(0) for a cinematic effect via view().
Effect page βStagger Reveal CSS
Item grid with incremental native CSS animation-delay and view() for a cascade effect.
Clip Path Reveal
Progressive reveal via clip-path: inset() animated by view() and cover range.
Effect page βColor Change
Progressive color change (purple, pink, cyan, green) linked to scroll via scroll(nearest).
Progress Bar
Scroll to see the bar fill up. Uses animation-timeline: scroll() to link the animation to the container scroll.
Progress Bar
Progress bar linked to scroll via animation-timeline: scroll(). Fills from 0% to 100% in pure CSS.
Effect page βView source code
<div class="demo-preview">
<div class="sdc-scroll-container">
<div class="sdc-progress-bar">
<div class="sdc-progress-fill"></div>
</div>
<div class="sdc-progress-content">
<h3>Progress Bar</h3>
<p>Scroll to see the bar fill up. Uses animation-timeline: scroll() to link the animation to the container scroll.</p>
<div class="sdc-progress-block"></div>
<div class="sdc-progress-block"></div>
<div class="sdc-progress-block"></div>
<div class="sdc-progress-block"></div>
</div>
</div>
<span class="sdc-scroll-hint">Scroll β</span>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 280px;
overflow: hidden;
position: relative;
background: rgb(10, 10, 15);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.sdc-scroll-container {
width: 100%;
height: 280px;
overflow-y: auto;
position: relative;
scroll-behavior: smooth;
}
.sdc-scroll-container::-webkit-scrollbar {
width: 4px;
}
.sdc-scroll-container::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
}
.sdc-scroll-container::-webkit-scrollbar-thumb {
background: rgba(139, 92, 246, 0.4);
border-radius: 2px;
}
.sdc-scroll-hint {
position: absolute;
bottom: 8px;
left: 50%;
transform: translateX(-50%);
font-size: 0.65rem;
color: rgba(255, 255, 255, 0.3);
z-index: 5;
pointer-events: none;
animation: sdcHintPulse 2s ease-in-out infinite;
}
.sdc-progress-bar {
position: sticky;
top: 0;
left: 0;
width: 100%;
height: 4px;
background: rgba(255, 255, 255, 0.1);
z-index: 10;
}
.sdc-progress-fill {
height: 100%;
background: linear-gradient(90deg, #8b5cf6, #ec4899);
transform-origin: left;
animation: sdcProgressGrow linear;
animation-timeline: scroll(nearest);
transform: scaleX(0);
}
.sdc-progress-content {
padding: 20px;
height: 800px;
display: flex;
flex-direction: column;
gap: 16px;
}
.sdc-progress-content h3 {
font-size: 1rem;
font-weight: 700;
color: #fff;
margin: 0;
}
.sdc-progress-content p {
font-size: 0.78rem;
color: rgba(255, 255, 255, 0.5);
margin: 0;
line-height: 1.6;
}
.sdc-progress-block {
height: 120px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
}
@keyframes sdcHintPulse {
0%,
100% {
opacity: 0.3;
}
50% {
opacity: 0.7;
}
}
@keyframes sdcProgressGrow {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}/* Pure CSS β no JavaScript required */Fade In Reveal
Elements that progressively appear via animation-timeline: view() and animation-range: entry.
Parallax Layers
Parallax layers with different speeds, driven entirely by scroll() in native CSS.
Scale on Scroll
Cards that scale from 70% to 100% when entering the view via animation-timeline: view().
Color Shift
Background gradient that evolves through 5 color palettes while scrolling, via animation-timeline: scroll().
Effect page βText Reveal
Words that slide up from the bottom and progressively reveal while scrolling with view().
Effect page βRotate on Scroll
Geometric shape with 720-degree rotation linked to scroll. Borders rotate in the opposite direction.
Slide In Cards
Cards that alternately slide in from left and right with view() and animation-range: entry.
Effect page βZoom & Blur
Circle that zooms in and unblurs at the scroll center, then continues zooming and reblurs on exit.
Effect page βHorizontal Scroll
Vertical scroll converted into horizontal card scrolling via animation-timeline: scroll().
Chapter 1: Introduction
Welcome to this demonstration of a scroll-linked progress bar. The bar at the top of this container progresses based on your scroll position.
This effect uses the CSS property animation-timeline: scroll() which links a @keyframes animation directly to the container's scroll position.
Chapter 2: How It Works
The scroll-timeline property creates a named timeline on the scrollable container. The progress-bar animation then uses this timeline to animate the transform: scaleX() property.
The result is a smooth progress bar that accurately represents the percentage of content read, without any JavaScript.
Chapter 3: Applications
Ideal for blog posts, documentation pages, tutorials, or any long-form content where the user wants to visualize their reading progress.
Compatible with modern browsers supporting Scroll-Driven Animations (Chrome 115+, Edge 115+).
Chapter 4: Conclusion
Scroll-linked animations open new possibilities for user experience, all in pure CSS with no impact on JavaScript performance.
Progress Bar
Reading progress bar driven by animation-timeline: scroll(), zero JavaScript.
Effect page βView source code
<div class="demo-preview">
<div class="progress-scroll-container">
<div class="slr-progress-bar-track">
<div class="slr-progress-bar-fill"></div>
</div>
<div class="progress-content">
<h3>Chapter 1: Introduction</h3>
<p>Welcome to this demonstration of a scroll-linked progress bar. The bar at the top of this container progresses based on your scroll position.</p>
<p>This effect uses the CSS property animation-timeline: scroll() which links a @keyframes animation directly to the container's scroll position.</p>
<h3>Chapter 2: How It Works</h3>
<p>The scroll-timeline property creates a named timeline on the scrollable container. The progress-bar animation then uses this timeline to animate the transform: scaleX() property.</p>
<p>The result is a smooth progress bar that accurately represents the percentage of content read, without any JavaScript.</p>
<h3>Chapter 3: Applications</h3>
<p>Ideal for blog posts, documentation pages, tutorials, or any long-form content where the user wants to visualize their reading progress.</p>
<p>Compatible with modern browsers supporting Scroll-Driven Animations (Chrome 115+, Edge 115+).</p>
<h3>Chapter 4: Conclusion</h3>
<p>Scroll-linked animations open new possibilities for user experience, all in pure CSS with no impact on JavaScript performance.</p>
</div>
</div>
<span class="slr-scroll-hint">Scroll to see the effect</span>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 280px;
overflow: hidden;
position: relative;
background: rgb(10, 10, 15);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.slr-scroll-hint {
position: absolute;
bottom: 8px;
left: 50%;
transform: translateX(-50%);
font-size: 0.7rem;
color: rgba(255, 255, 255, 0.4);
pointer-events: none;
z-index: 10;
animation: hintBounce 2s ease-in-out infinite;
}
.progress-scroll-container {
width: 100%;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
position: relative;
scroll-timeline: --progressScroll y;
}
.slr-progress-bar-track {
position: sticky;
top: 0;
left: 0;
width: 100%;
height: 4px;
background: rgba(255, 255, 255, 0.1);
z-index: 5;
}
.slr-progress-bar-fill {
height: 100%;
background: linear-gradient(90deg, #6366f1, #d946ef, #f59e0b);
transform-origin: left;
transform: scaleX(0);
animation: progressGrow linear both;
animation-timeline: --progressScroll;
}
.progress-content {
padding: 16px 12px;
}
.progress-content p {
font-size: 0.8rem;
line-height: 1.6;
color: rgba(255, 255, 255, 0.7);
margin-bottom: 12px;
}
.progress-content h3 {
font-size: 0.9rem;
color: #a78bfa;
margin-bottom: 8px;
}
@keyframes hintBounce {
0%,
100% {
transform: translateX(-50%) translateY(0);
}
50% {
transform: translateX(-50%) translateY(-4px);
}
}
@keyframes progressGrow {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}/* Pure CSS β no JavaScript required */Scroll Fade Gallery
Images that fade in and scale up on viewport entry via animation-timeline: view().
Effect page βView source code
<div class="demo-preview">
<div class="fade-gallery-container">
<div class="scroll-spacer"></div>
<div class="fade-gallery-item"></div>
<div class="fade-gallery-item"></div>
<div class="fade-gallery-item"></div>
<div class="fade-gallery-item"></div>
<div class="fade-gallery-item"></div>
<div class="scroll-spacer"></div>
</div>
<span class="slr-scroll-hint">Scroll to see the effect</span>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 280px;
overflow: hidden;
position: relative;
background: rgb(10, 10, 15);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.slr-scroll-hint {
position: absolute;
bottom: 8px;
left: 50%;
transform: translateX(-50%);
font-size: 0.7rem;
color: rgba(255, 255, 255, 0.4);
pointer-events: none;
z-index: 10;
animation: hintBounce 2s ease-in-out infinite;
}
.scroll-spacer {
height: 60px;
}
.fade-gallery-container {
width: 100%;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
padding: 20px 12px;
}
.fade-gallery-item {
width: 80%;
margin: 24px auto;
aspect-ratio: 16/9;
border-radius: 10px;
opacity: 0;
transform: scale(0.8) translateY(20px);
animation: fadeScaleIn linear both;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}
/* Les fonds comptent les enfants du dΓ©fileur : le premier enfant est l'espaceur,
les cinq blocs sont donc les enfants 2 Γ 6. */
.fade-gallery-item:nth-child(2) {
background: linear-gradient(135deg, #ec4899, #f43f5e);
}
.fade-gallery-item:nth-child(3) {
background: linear-gradient(135deg, #06b6d4, #3b82f6);
}
.fade-gallery-item:nth-child(4) {
background: linear-gradient(135deg, #10b981, #059669);
}
.fade-gallery-item:nth-child(5) {
background: linear-gradient(135deg, #f59e0b, #ea580c);
}
.fade-gallery-item:nth-child(6) {
background: linear-gradient(135deg, #6366f1, #8b5cf6);
}
@keyframes hintBounce {
0%,
100% {
transform: translateX(-50%) translateY(0);
}
50% {
transform: translateX(-50%) translateY(-4px);
}
}
@keyframes fadeScaleIn {
from {
opacity: 0;
transform: scale(0.8) translateY(20px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}/* Pure CSS β no JavaScript required */Horizontal Scroll Section
Section scrolling horizontally during vertical scroll, pure CSS with scroll-timeline.
Effect page βSticky Reveal Stack
Cards stacking and revealing one by one on scroll with view() timeline.
Text Line Reveal
Each word progressively colorizes while scrolling via animation-range and view().
Effect page βZoom Into Content
Content starting zoomed out that scales to 100% on scroll, cinematic opening effect.
Rotation on Scroll
Element rotating proportionally to scroll position, dial/compass effect.
Effect page βPath Drawing on Scroll
SVG path drawing itself via stroke-dashoffset animated by scroll, pure CSS.
Effect page βParallax Layers
Multi-layer parallax entirely controlled by scroll() timeline, no JS.
Effect page βSnap Gallery with Progress
Snap-scrolling gallery with progress bar and dots linked to scroll, pure CSS.
Effect page βIntroduction
Scrollytelling is a narrative technique that uses page scrolling to guide the user through an immersive visual story.
Chapter 1
Scroll-triggered animations progressively reveal content, creating a natural reading rhythm that captivates attention.
Chapter 2
By combining parallax, transitions, and visual effects, each section becomes a scene in a larger narrative, transforming a simple website into a storytelling experience.
Chapter 3
The best scrollytelling examples balance aesthetics and functionality, guiding the user without overwhelming them with excessive effects.
Conclusion
Master these techniques to create memorable web experiences that tell your story with elegance and fluidity.
Progress Bar
Horizontal progress bar that fills proportionally to content scrolling.
Effect page βView source code
<div class="demo-preview">
<div class="st-progress-scroll">
<div class="scroll-container" id="progress-scroll">
<div class="st-progress-bar-track">
<div class="st-progress-bar-fill" id="progress-fill"></div>
</div>
<div class="progress-inner">
<h3>Introduction</h3>
<p>Scrollytelling is a narrative technique that uses page scrolling to guide the user through an immersive visual story.</p>
<div class="section-divider"></div>
<h3>Chapter 1</h3>
<p>Scroll-triggered animations progressively reveal content, creating a natural reading rhythm that captivates attention.</p>
<div class="section-divider"></div>
<h3>Chapter 2</h3>
<p>By combining parallax, transitions, and visual effects, each section becomes a scene in a larger narrative, transforming a simple website into a storytelling experience.</p>
<div class="section-divider"></div>
<h3>Chapter 3</h3>
<p>The best scrollytelling examples balance aesthetics and functionality, guiding the user without overwhelming them with excessive effects.</p>
<div class="section-divider"></div>
<h3>Conclusion</h3>
<p>Master these techniques to create memorable web experiences that tell your story with elegance and fluidity.</p>
</div>
</div>
<span class="scroll-hint-label">Scroll to explore</span>
</div>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 280px;
overflow: hidden;
position: relative;
background: rgb(10, 10, 15);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.st-progress-scroll {
position: relative;
display: flex;
flex-direction: column;
width: 100%;
max-height: 100%;
background: #0a0a0f;
color: #fff;
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.scroll-container {
width: 100%;
height: 280px;
overflow: hidden auto;
position: relative;
scroll-behavior: smooth;
}
.scroll-container::-webkit-scrollbar {
width: 4px;
}
.scroll-container::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.03);
}
.scroll-container::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.15);
border-radius: 4px;
}
.scroll-container::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.3);
}
.scroll-hint-label {
position: absolute;
bottom: 8px;
left: 50%;
transform: translateX(-50%);
font-size: 0.65rem;
color: rgba(255, 255, 255, 0.3);
text-transform: uppercase;
letter-spacing: 0.1em;
pointer-events: none;
z-index: 10;
animation: pulseHint 2s ease-in-out infinite;
}
.st-progress-bar-track {
position: sticky;
top: 0;
left: 0;
width: 100%;
height: 4px;
background: rgba(255, 255, 255, 0.08);
z-index: 20;
}
.st-progress-bar-fill {
height: 100%;
width: 0%;
background: linear-gradient(90deg, #6366f1, #a855f7, #ec4899);
border-radius: 0 2px 2px 0;
transition: width 0.05s linear;
}
.progress-inner {
padding: 24px 20px;
}
.progress-inner h3 {
font-size: 1rem;
font-weight: 700;
color: #fff;
margin: 0 0 12px;
}
.progress-inner p {
font-size: 0.8rem;
color: rgba(255, 255, 255, 0.5);
line-height: 1.7;
margin: 0 0 16px;
}
.progress-inner .section-divider {
width: 60px;
height: 2px;
background: linear-gradient(90deg, #6366f1, transparent);
margin: 20px 0;
}
@keyframes pulseHint {
0%,
100% {
opacity: 0.3;
}
50% {
opacity: 0.7;
}
}(function() {
/* fx-0575 β Introduction (Progress Bar) */
(function() {
const progressScroll = document.getElementById('progress-scroll');
const progressFill = document.getElementById('progress-fill');
if (progressScroll && progressFill) {
progressScroll.addEventListener('scroll', () => {
const scrollTop = progressScroll.scrollTop;
const scrollHeight = progressScroll.scrollHeight - progressScroll.clientHeight;
const progress = scrollHeight > 0 ? (scrollTop / scrollHeight) * 100 : 0;
progressFill.style.width = progress + '%';
});
}
})();
})();Parallax Layers
Three visual layers move at different speeds to create an immersive depth effect.
Effect page βReveal on Scroll
Elements appear with a fade and vertical slide when they enter the visible area.
Effect page βSticky Horizontal
Automatic horizontal scrolling simulating panel navigation, ideal for presenting steps or projects.
Effect page βText Highlight on Scroll
Each word progressively lights up as you scroll, creating a guided reading effect.
Effect page βZoom on Scroll
An element progressively grows with scrolling, going from a small object to a full-screen view.
Effect page βNumber Counter Scroll
Counters animate from zero to their target value proportionally to the scroll position.
Effect page βTimeline Scroll
Interactive vertical timeline with a line that progressively draws and elements that appear on scroll.
Effect page βImage Sequence
Frame-by-frame animation simulation driven by scroll, similar to Apple product pages.
Effect page βScroll Snap Sections
Full-height sections with magnetic snapping and side pagination indicator.
Effect page βSticky Scroll
8 advanced sticky scroll and parallax effects.
First Section
The image stays fixed while content scrolls. Ideal technique for presenting features.
Second Section
Each text section triggers a visual change in the sticky image.
Third Section
Smooth transitions guide the user through content seamlessly.
Fourth Section
A pattern used in interactive journalism and product websites.
Sticky Image Change
Sticky image that changes while content scrolls with panels revealed via view().
Effect page βView source code
<div class="demo-preview">
<div class="sdc-scroll-container">
<div class="ss-imgchange-content">
<div class="ss-imgchange-display">01</div>
<div class="ss-imgchange-panels">
<div class="ss-imgchange-panel"><h4>First Section</h4><p>The image stays fixed while content scrolls. Ideal technique for presenting features.</p></div>
<div class="ss-imgchange-panel"><h4>Second Section</h4><p>Each text section triggers a visual change in the sticky image.</p></div>
<div class="ss-imgchange-panel"><h4>Third Section</h4><p>Smooth transitions guide the user through content seamlessly.</p></div>
<div class="ss-imgchange-panel"><h4>Fourth Section</h4><p>A pattern used in interactive journalism and product websites.</p></div>
</div>
</div>
</div>
<span class="sdc-scroll-hint">Scroll β</span>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 280px;
overflow: hidden;
position: relative;
background: rgb(10, 10, 15);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.ss-imgchange-content {
height: 1200px;
padding: 20px 12px;
}
.ss-imgchange-display {
width: 100%;
height: 120px;
border-radius: 14px;
background: linear-gradient(135deg, #6366f1, #8b5cf6);
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
font-weight: 800;
color: white;
position: sticky;
top: 10px;
z-index: 2;
animation: ssImgChange linear;
animation-timeline: scroll(nearest);
}
.ss-imgchange-panels {
padding-top: 20px;
display: flex;
flex-direction: column;
gap: 16px;
}
.ss-imgchange-panel {
padding: 16px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
opacity: 0;
transform: translateY(20px);
animation: sbFadeUp linear both;
animation-timeline: view();
animation-range: entry 0% entry 50%;
}
.ss-imgchange-panel h4 {
font-size: 0.85rem;
font-weight: 700;
color: #fff;
margin: 0 0 6px;
}
.ss-imgchange-panel p {
font-size: 0.72rem;
color: rgba(255, 255, 255, 0.5);
margin: 0;
line-height: 1.5;
}
.sdc-scroll-container {
width: 100%;
height: 280px;
overflow-y: auto;
position: relative;
scroll-behavior: smooth;
}
.sdc-scroll-container::-webkit-scrollbar {
width: 4px;
}
.sdc-scroll-container::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
}
.sdc-scroll-container::-webkit-scrollbar-thumb {
background: rgba(139, 92, 246, 0.4);
border-radius: 2px;
}
.sdc-scroll-hint {
position: absolute;
bottom: 8px;
left: 50%;
transform: translateX(-50%);
font-size: 0.65rem;
color: rgba(255, 255, 255, 0.3);
z-index: 5;
pointer-events: none;
animation: sdcHintPulse 2s ease-in-out infinite;
}
@keyframes sbFadeUp {
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes ssImgChange {
0%,
20% {
background: linear-gradient(135deg, #6366f1, #8b5cf6);
box-shadow: 0 8px 30px rgba(99, 102, 241, 0.3);
}
25%,
45% {
background: linear-gradient(135deg, #8b5cf6, #d946ef);
box-shadow: 0 8px 30px rgba(139, 92, 246, 0.3);
}
50%,
70% {
background: linear-gradient(135deg, #d946ef, #f43f5e);
box-shadow: 0 8px 30px rgba(217, 70, 239, 0.3);
}
75%,
100% {
background: linear-gradient(135deg, #f43f5e, #f59e0b);
box-shadow: 0 8px 30px rgba(244, 63, 94, 0.3);
}
}
@keyframes sdcHintPulse {
0%,
100% {
opacity: 0.3;
}
50% {
opacity: 0.7;
}
}/* Pure CSS β no JavaScript required */Horizontal Scroll
Horizontal scrolling section with snap to navigate between workflow steps.
Effect page βCard Stack
Cards that stack with position sticky and view() animation in the container.
Effect page βProgress Reveal
Sticky progress bar with items progressively revealed via scroll(nearest) and view().
Split Screen Sticky
Sticky visual with rotating SVG and content revealed by scroll via view() and scroll(nearest).
Effect page βZoom In Sticky
Element that progressively zooms from scale(1) to scale(2.2) via scroll(nearest).
Effect page βText Highlight Scroll
Words colored sequentially on scroll with JS scoped to the container.
Effect page βLayered Parallax
5 layers moving at different speeds via scroll(nearest) for a complete parallax effect.
Effect page βScroll Snap
6 scroll snap effects for precise navigation.
Section 1
Scroll to move to the next one
Section 2
Scroll stops automatically
Section 3
Smooth and controlled experience
Section 4
Last section
Vertical Snap
Sections automatically snapping to start via scroll-snap-type: y mandatory.
Effect page βView source code
<div class="demo-preview">
<div class="sn-vsnap-wrap">
<div class="sn-vsnap-container">
<div class="sn-vsnap-section"><h4>Section 1</h4><p>Scroll to move to the next one</p></div>
<div class="sn-vsnap-section"><h4>Section 2</h4><p>Scroll stops automatically</p></div>
<div class="sn-vsnap-section"><h4>Section 3</h4><p>Smooth and controlled experience</p></div>
<div class="sn-vsnap-section"><h4>Section 4</h4><p>Last section</p></div>
</div>
<span class="sdc-scroll-hint">Scroll β</span>
</div>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 280px;
overflow: hidden;
position: relative;
background: rgb(10, 10, 15);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.sn-vsnap-wrap {
position: relative;
width: 100%;
background: #1e1b4b;
color: #fff;
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.sn-vsnap-container {
width: 100%;
height: 280px;
overflow-y: auto;
scroll-snap-type: y mandatory;
scroll-behavior: smooth;
}
.sn-vsnap-section {
box-sizing: border-box;
height: 280px;
scroll-snap-align: start;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
padding: 20px;
text-align: center;
}
.sn-vsnap-section:nth-child(1) {
background: linear-gradient(135deg, #1e1b4b, #312e81);
}
.sn-vsnap-section:nth-child(2) {
background: linear-gradient(135deg, #312e81, #4c1d95);
}
.sn-vsnap-section:nth-child(3) {
background: linear-gradient(135deg, #4c1d95, #6d28d9);
}
.sn-vsnap-section:nth-child(4) {
background: linear-gradient(135deg, #6d28d9, #7c3aed);
}
.sn-vsnap-section h4 {
font-size: 0.9rem;
font-weight: 700;
color: white;
margin: 0;
}
.sn-vsnap-section p {
font-size: 0.72rem;
color: rgba(255, 255, 255, 0.6);
margin: 0;
}
.sdc-scroll-hint {
position: absolute;
bottom: 8px;
left: 50%;
transform: translateX(-50%);
font-size: 0.65rem;
color: rgba(255, 255, 255, 0.3);
z-index: 5;
pointer-events: none;
animation: sdcHintPulse 2s ease-in-out infinite;
}
@keyframes sdcHintPulse {
0%,
100% {
opacity: 0.3;
}
50% {
opacity: 0.7;
}
}/* Pure CSS β no JavaScript required */Horizontal Snap
Horizontal scrolling with automatic snapping via scroll-snap-type: x mandatory.
Effect page βMixed Content Snap
Sections of different sizes with scroll-snap-type: y proximity for flexibility.
Effect page βSnap + Progress
Vertical snap with progress bar and indicator dots via JS scoped to the container.
Effect page βSmooth Snap
Snap with animations triggered by IntersectionObserver scoped to the container.
Effect page β3D Depth Gallery
Immersive Three.js gallery: the camera dives through a stack of images, smooth crossfade, background tinted by the dominant colour, cursor parallax. Cinematic depth sensation.
π‘ Perfect for: creative portfolio, product showcase, case study gallery, "our work" page, scroll storytelling.
Effect page βChapter 1 β The concept
Scroll inside this area: the glowing bar on the left fills at the exact pace of your reading, with a halo that reacts to position. The further you go, the higher the beam rises.
Chapter 2 β The marker
The luminous cursor slides along the vertical axis. It's an elegant and discreet progress marker, perfect for a long article, documentation page, or a narrative that unfolds on scroll.
Chapter 3 β The mechanics
No external dependency: a simple scroll listener calculates the scrolled / total height ratio and updates a CSS variable. Lightweight, smooth, and fully accessible.
Chapter 4 β Performance
The listener is passive and only touches a custom property, so zero costly reflow. Rendering stays at 60 fps even on mobile, and the bar follows the finger to the pixel.
Chapter 5 β Accessibility
The bar is purely decorative (aria-hidden): content remains fully readable and keyboard-navigable. In reduced-motion mode, instant scrolling replaces smooth-scroll.
Chapter 6 β The end
At the very bottom, the bar is full at 100% and the percentage confirms it. A clear marker from start to finish, without ever stealing attention from the content.
Scroll Progress Beam
A glowing lateral bar that fills with scroll progress, reactive halo and sliding cursor. Premium, discreet reading marker.
π‘ Perfect for: article progress bar, long page / docs, multi-section onboarding, storytelling landing.
Effect page βView source code
<div class="demo-preview">
<div class="spb-scene">
<div class="spb-beam" aria-hidden="true"><div class="spb-beam-fill"></div><div class="spb-beam-dot"></div></div>
<div class="spb-pct" aria-hidden="true">0%</div>
<div class="spb-scroll">
<div class="spb-block">
<h4>Chapter 1 β The concept</h4>
<p>Scroll inside this area: the glowing bar on the left fills at the exact pace of your reading, with a halo that reacts to position. The further you go, the higher the beam rises.</p>
<h4>Chapter 2 β The marker</h4>
<p>The luminous cursor slides along the vertical axis. It's an elegant and discreet progress marker, perfect for a long article, documentation page, or a narrative that unfolds on scroll.</p>
<h4>Chapter 3 β The mechanics</h4>
<p>No external dependency: a simple scroll listener calculates the scrolled / total height ratio and updates a CSS variable. Lightweight, smooth, and fully accessible.</p>
<h4>Chapter 4 β Performance</h4>
<p>The listener is passive and only touches a custom property, so zero costly reflow. Rendering stays at 60 fps even on mobile, and the bar follows the finger to the pixel.</p>
<h4>Chapter 5 β Accessibility</h4>
<p>The bar is purely decorative (aria-hidden): content remains fully readable and keyboard-navigable. In reduced-motion mode, instant scrolling replaces smooth-scroll.</p>
<h4>Chapter 6 β The end</h4>
<p>At the very bottom, the bar is full at 100% and the percentage confirms it. A clear marker from start to finish, without ever stealing attention from the content.</p>
</div>
</div>
</div>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 280px;
overflow: hidden;
position: relative;
background: rgb(10, 10, 15);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.spb-scene {
position: relative;
width: 100%;
aspect-ratio: 16 / 10;
max-height: 340px;
border-radius: 14px;
overflow: hidden;
isolation: isolate;
background: #0c1022;
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.spb-scroll {
position: absolute;
inset: 0;
overflow-y: auto;
scroll-behavior: smooth;
padding: 22px 22px 22px 40px;
box-sizing: border-box;
}
.spb-scroll::-webkit-scrollbar {
width: 0;
}
.spb-scroll {
scrollbar-width: none;
}
@media (prefers-reduced-motion: reduce) {
.spb-scroll {
scroll-behavior: auto;
}
}
.spb-block {
color: #e7e9f5;
}
.spb-block h4 {
margin: 0 0 8px;
font-size: 16px;
}
.spb-block p {
margin: 0 0 22px;
font-size: 13px;
line-height: 1.7;
color: #aeb2c8;
}
.spb-beam {
position: absolute;
left: 14px;
top: 18px;
bottom: 18px;
width: 4px;
border-radius: 4px;
background: rgba(255, 255, 255, 0.10);
z-index: 3;
pointer-events: none;
}
.spb-beam-fill {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: calc(var(--spb-p, 0) * 100%);
border-radius: 4px;
background: linear-gradient(180deg, #5cffd8, #6d8bff);
box-shadow: 0 0 12px rgba(109, 139, 255, 0.8), 0 0 4px rgba(92, 255, 216, 0.9);
}
.spb-beam-dot {
position: absolute;
left: 50%;
top: calc(var(--spb-p, 0) * 100%);
width: 12px;
height: 12px;
border-radius: 50%;
transform: translate(-50%, -50%);
background: #fff;
box-shadow: 0 0 14px 3px rgba(109, 139, 255, 0.9);
}
.spb-pct {
position: absolute;
right: 14px;
top: 12px;
z-index: 3;
font-size: 12px;
font-variant-numeric: tabular-nums;
color: #aeb2c8;
background: rgba(0, 0, 0, 0.4);
padding: 3px 9px;
border-radius: 999px;
pointer-events: none;
}(function() {
/* fx-0793 β Scroll Progress Beam (IIFE scopΓ©e) */
(function() {
document.querySelectorAll('.spb-scene').forEach(function(scene) {
var scroller = scene.querySelector('.spb-scroll');
var beam = scene.querySelector('.spb-beam');
var pct = scene.querySelector('.spb-pct');
if (!scroller || !beam) return;
function update() {
var max = scroller.scrollHeight - scroller.clientHeight;
var p = max > 0 ? scroller.scrollTop / max : 0;
beam.style.setProperty('--spb-p', p.toFixed(4));
if (pct) pct.textContent = Math.round(p * 100) + '%';
}
scroller.addEventListener('scroll', update, {
passive: true
});
window.addEventListener('resize', update);
update();
});
})();
})();SVG Blinds Mask
Blinds open slat by slat at the exact pace of your scrolling, revealing the scene. Each slat is phased by its index β direct scroll β CSS mapping, no forced animation.
π‘ Perfect for: revealing a visual hero, a product image, a portfolio section on scroll.
Effect page βView source code
<div class="demo-preview">
<div class="sbm-scene">
<div class="sbm-scroll" id="sbmScroll" tabindex="0" role="region" aria-label="Scrollable demo area">
<div class="sbm-track" style="height:340%"></div>
</div>
<div class="sbm-stage" aria-hidden="true">
<div class="sbm-photo">
<div class="sbm-caption">REVEALED</div>
</div>
<div class="sbm-slats">
<span class="sbm-slat" style="--k:0"></span>
<span class="sbm-slat" style="--k:1"></span>
<span class="sbm-slat" style="--k:2"></span>
<span class="sbm-slat" style="--k:3"></span>
<span class="sbm-slat" style="--k:4"></span>
<span class="sbm-slat" style="--k:5"></span>
<span class="sbm-slat" style="--k:6"></span>
<span class="sbm-slat" style="--k:7"></span>
</div>
</div>
<span class="sbm-hint">Scroll here β</span>
</div>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 280px;
overflow: hidden;
position: relative;
background: rgb(10, 10, 15);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.sbm-scene {
position: relative;
width: 100%;
max-width: 430px;
height: 260px;
border-radius: 12px;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, .08);
background: #0d0d16;
--p: 0
}
.sbm-scroll {
position: absolute;
inset: 0;
overflow-y: auto;
z-index: 3;
scrollbar-width: thin
}
.sbm-stage {
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none
}
.sbm-hint {
position: absolute;
right: 10px;
bottom: 8px;
z-index: 2;
font-size: .6rem;
letter-spacing: .12em;
text-transform: uppercase;
color: #8f8fb0;
opacity: calc(1 - var(--p) * 3);
pointer-events: none
}
.sbm-scroll:focus-visible {
outline: 2px solid #a78bfa;
outline-offset: -2px
}
.sbm-track {
width: 100%
}
.sbm-photo {
position: absolute;
inset: 0;
display: grid;
place-items: center;
background: radial-gradient(circle at 30% 25%, rgba(34, 255, 170, .25), transparent 55%), linear-gradient(160deg, #143a5e, #7c3aed 70%, #db2777)
}
.sbm-caption {
font-size: 1.3rem;
font-weight: 800;
letter-spacing: .3em;
color: #fff;
text-shadow: 0 2px 12px rgba(0, 0, 0, .6);
opacity: clamp(0, (var(--p) - .75) * 4, 1)
}
.sbm-slats {
position: absolute;
inset: 0;
display: flex;
flex-direction: column
}
.sbm-slat {
flex: 1;
background: #0d0d16;
transform: scaleY(clamp(0, 1 - (var(--p) * 9 - var(--k)), 1));
transform-origin: 50% 50%
}(function() {
(function() {
/* fx-0778 β SVG Blinds Mask : progression de scroll β variable CSS --p (rAF, passif) */
var sc = document.getElementById('sbmScroll');
if (!sc) return;
var host = sc.closest('.sbm-scene');
var raf = null;
function update() {
raf = null;
var max = sc.scrollHeight - sc.clientHeight;
var p = max > 0 ? sc.scrollTop / max : 0;
host.style.setProperty('--p', p.toFixed(4));
}
sc.addEventListener('scroll', function() {
if (!raf) raf = requestAnimationFrame(update);
}, {
passive: true
});
update();
})();
})();Corner Morph Overlay
A circle grows from the corner and swallows the screen as you scroll, revealing the next section β a scroll-driven chapter transition, pure clip-path.
π‘ Perfect for: section transitions, chaptered storytelling, narrative landing pages.
Effect page βView source code
<div class="demo-preview">
<div class="cmo-scene">
<div class="cmo-scroll" id="cmoScroll" tabindex="0" role="region" aria-label="Scrollable demo area">
<div class="cmo-track" style="height:340%"></div>
</div>
<div class="cmo-stage" aria-hidden="true">
<div class="cmo-a"><h4>Chapter I</h4><p>Keep scrollingβ¦</p></div>
<div class="cmo-b"><h4>Chapter II</h4><p>β¦the corner swallowed it all.</p></div>
</div>
<span class="cmo-hint">Scroll here β</span>
</div>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 280px;
overflow: hidden;
position: relative;
background: rgb(10, 10, 15);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.cmo-scene {
position: relative;
width: 100%;
max-width: 430px;
height: 260px;
border-radius: 12px;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, .08);
background: #0d0d16;
--p: 0
}
.cmo-scroll {
position: absolute;
inset: 0;
overflow-y: auto;
z-index: 3;
scrollbar-width: thin
}
.cmo-stage {
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none
}
.cmo-hint {
position: absolute;
right: 10px;
bottom: 8px;
z-index: 2;
font-size: .6rem;
letter-spacing: .12em;
text-transform: uppercase;
color: #8f8fb0;
opacity: calc(1 - var(--p) * 3);
pointer-events: none
}
.cmo-scroll:focus-visible {
outline: 2px solid #a78bfa;
outline-offset: -2px
}
.cmo-track {
width: 100%
}
.cmo-a,
.cmo-b {
position: absolute;
inset: 0;
display: grid;
place-content: center;
text-align: center;
gap: 4px
}
.cmo-a {
background: linear-gradient(160deg, #16162a, #101020);
color: #cfcfe8
}
.cmo-b {
background: radial-gradient(circle at 88% 88%, rgba(255, 209, 102, .25), transparent 50%), linear-gradient(200deg, #2b1440, #7c3aed);
color: #fff;
clip-path: circle(calc(6% + var(--p) * 140%) at 90% 88%)
}
.cmo-a h4,
.cmo-b h4 {
margin: 0;
font-size: 1.05rem;
letter-spacing: .18em;
text-transform: uppercase
}
.cmo-a p,
.cmo-b p {
margin: 0;
font-size: .72rem;
opacity: .75
}(function() {
(function() {
/* fx-0779 β Corner Morph Overlay : progression de scroll β variable CSS --p (rAF, passif) */
var sc = document.getElementById('cmoScroll');
if (!sc) return;
var host = sc.closest('.cmo-scene');
var raf = null;
function update() {
raf = null;
var max = sc.scrollHeight - sc.clientHeight;
var p = max > 0 ? sc.scrollTop / max : 0;
host.style.setProperty('--p', p.toFixed(4));
}
sc.addEventListener('scroll', function() {
if (!raf) raf = requestAnimationFrame(update);
}, {
passive: true
});
update();
})();
})();Corner Morph Cards
Three scattered cards with organic corners straighten, align and soften their angles as you scroll β the assembly is pure border-radius and transform.
π‘ Perfect for: product grid intros, portfolios, βour servicesβ sections.
Effect page βView source code
<div class="demo-preview">
<div class="cmc-scene">
<div class="cmc-scroll" id="cmcScroll" tabindex="0" role="region" aria-label="Scrollable demo area">
<div class="cmc-track" style="height:340%"></div>
</div>
<div class="cmc-stage" aria-hidden="true">
<div class="cmc-row">
<div class="cmc-card" style="--r:-9deg; --g:linear-gradient(145deg,#22ffaa,#0e7490)">01</div>
<div class="cmc-card" style="--r:5deg; --g:linear-gradient(145deg,#7c3aed,#db2777)">02</div>
<div class="cmc-card" style="--r:11deg; --g:linear-gradient(145deg,#ffd166,#f4a300)">03</div>
</div>
</div>
<span class="cmc-hint">Scroll here β</span>
</div>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 280px;
overflow: hidden;
position: relative;
background: rgb(10, 10, 15);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.cmc-scene {
position: relative;
width: 100%;
max-width: 430px;
height: 260px;
border-radius: 12px;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, .08);
background: #0d0d16;
--p: 0
}
.cmc-scroll {
position: absolute;
inset: 0;
overflow-y: auto;
z-index: 3;
scrollbar-width: thin
}
.cmc-stage {
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none
}
.cmc-hint {
position: absolute;
right: 10px;
bottom: 8px;
z-index: 2;
font-size: .6rem;
letter-spacing: .12em;
text-transform: uppercase;
color: #8f8fb0;
opacity: calc(1 - var(--p) * 3);
pointer-events: none
}
.cmc-scroll:focus-visible {
outline: 2px solid #a78bfa;
outline-offset: -2px
}
.cmc-track {
width: 100%
}
.cmc-row {
position: absolute;
inset: 0;
display: flex;
gap: calc(34px - var(--p) * 22px);
align-items: center;
justify-content: center
}
.cmc-card {
width: 86px;
height: 116px;
display: grid;
place-items: center;
background: var(--g);
color: rgba(255, 255, 255, .9);
font-weight: 800;
font-size: 1.1rem;
border-radius: calc(50px - var(--p) * 40px) calc(14px + var(--p) * 0px) calc(46px - var(--p) * 36px) calc(18px - var(--p) * 8px);
transform: rotate(calc(var(--r) * (1 - var(--p)))) translateY(calc((1 - var(--p)) * 12px));
box-shadow: 0 10px 24px rgba(0, 0, 0, .45)
}(function() {
(function() {
/* fx-0780 β Corner Morph Cards : progression de scroll β variable CSS --p (rAF, passif) */
var sc = document.getElementById('cmcScroll');
if (!sc) return;
var host = sc.closest('.cmc-scene');
var raf = null;
function update() {
raf = null;
var max = sc.scrollHeight - sc.clientHeight;
var p = max > 0 ? sc.scrollTop / max : 0;
host.style.setProperty('--p', p.toFixed(4));
}
sc.addEventListener('scroll', function() {
if (!raf) raf = requestAnimationFrame(update);
}, {
passive: true
});
update();
})();
})();Concentric Rings Morph
Five concentric rings draw themselves, rotate and shift hue (per-index OKLCH) at the exact pace of your scroll, around a live progress percentage.
π‘ Perfect for: reading progress, onboarding steps, immersive scroll counters.
Effect page βView source code
<div class="demo-preview">
<div class="crm-scene">
<div class="crm-scroll" id="crmScroll" tabindex="0" role="region" aria-label="Scrollable demo area">
<div class="crm-track" style="height:340%"></div>
</div>
<div class="crm-stage" aria-hidden="true">
<svg class="crm-svg" viewBox="0 0 120 120" aria-hidden="true">
<circle class="crm-ring" style="--i:0" cx="60" cy="60" r="14"/>
<circle class="crm-ring" style="--i:1" cx="60" cy="60" r="25"/>
<circle class="crm-ring" style="--i:2" cx="60" cy="60" r="36"/>
<circle class="crm-ring" style="--i:3" cx="60" cy="60" r="47"/>
<circle class="crm-ring" style="--i:4" cx="60" cy="60" r="58"/>
</svg>
<span class="crm-pct"></span>
</div>
<span class="crm-hint">Scroll here β</span>
</div>
</div>.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
box-sizing: border-box
}
.demo-preview {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 280px;
overflow: hidden;
position: relative;
background: rgb(10, 10, 15);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.crm-scene {
position: relative;
width: 100%;
max-width: 430px;
height: 260px;
border-radius: 12px;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, .08);
background: #0d0d16;
--p: 0
}
.crm-scroll {
position: absolute;
inset: 0;
overflow-y: auto;
z-index: 3;
scrollbar-width: thin
}
.crm-stage {
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none
}
.crm-hint {
position: absolute;
right: 10px;
bottom: 8px;
z-index: 2;
font-size: .6rem;
letter-spacing: .12em;
text-transform: uppercase;
color: #8f8fb0;
opacity: calc(1 - var(--p) * 3);
pointer-events: none
}
.crm-scroll:focus-visible {
outline: 2px solid #a78bfa;
outline-offset: -2px
}
.crm-track {
width: 100%
}
.crm-svg {
position: absolute;
inset: 0;
margin: auto;
width: 200px;
height: 200px
}
.crm-ring {
fill: none;
stroke-width: 5;
stroke-linecap: round;
stroke: oklch(75% .26 calc(280 + var(--i) * 26 + var(--p) * 120));
stroke-dasharray: calc((88 + var(--i) * 69) * var(--p) * 1px) 1000px;
transform-origin: 60px 60px;
transform: rotate(calc(-90deg + var(--i) * 18deg * var(--p))) scale(calc(.72 + var(--p) * .28));
opacity: calc(.35 + var(--p) * .65)
}
.crm-pct {
position: absolute;
inset: 0;
display: grid;
place-items: center;
font-size: 1rem;
font-weight: 800;
color: #e7e5ff;
letter-spacing: .08em
}
.crm-pct::after {
content: attr(data-pct)
}(function() {
(function() {
/* fx-0781 β Concentric Rings Morph : progression de scroll β variable CSS --p (rAF, passif) */
var sc = document.getElementById('crmScroll');
if (!sc) return;
var host = sc.closest('.crm-scene');
var raf = null;
function update() {
raf = null;
var max = sc.scrollHeight - sc.clientHeight;
var p = max > 0 ? sc.scrollTop / max : 0;
host.style.setProperty('--p', p.toFixed(4));
var pct = host.querySelector('.crm-pct');
if (pct) pct.setAttribute('data-pct', Math.round(p * 100) + '%');
}
sc.addEventListener('scroll', function() {
if (!raf) raf = requestAnimationFrame(update);
}, {
passive: true
});
update();
})();
})();Scroll Velocity Feedback
Elements react to scroll speed: dynamic stretching and tilt that give physical weight to scrolling, then snapping back to sharp at rest.
π‘ Perfect for: editorial gallery, project grid, immersive feed, creative "about" page.
Effect page βPinned Image Sequence
A pinned object whose 360Β° rotation is driven by scroll, Apple-style product sequence. Here, a real 3D model (.glb, self-hosted, loaded on demand) rotates as you scroll.
π‘ Perfect for: 360Β° product view, feature/teardown reveal, launch page, step-by-step visual tutorial.
Effect page βSticky Horizontal Reveal
A pinned section whose content scrolls horizontally as you scroll vertically. Tells a progression (process, timeline, steps) in a cinematic way.
π‘ Perfect for: process/step presentation, timeline, portfolio showcase, narrative product page.
Effect page βScroll Choreography Sequence
Multi-element orchestration: headline, text, badges and CTA enter in cascade on the scroll timeline, like a scene being built. Each element has its own progress window.
π‘ Perfect for: assembling hero section, feature presentation, brand storytelling, "manifesto" section.
Effect page β