Advertisement

Scroll-Driven View Transitions

| by Vladimir | 3 min read | code by Giana
A11y Ready Intermediate

Tech & Dependencies

HTML CSS

Features

  • Scroll Timeline
  • Scroll Snap
  • View Transitions
  • Pure CSS

Browser Support

Chrome 115+ Edge 115+

Core

This is a Scroll-Driven View Transitions component. It simulates app-like, full-screen transitions between content sections using only native CSS properties. Its function is to replace traditional vertical scrolling with a more engaging, cinematic experience, where each scroll action triggers a complex animation (like a “melt”, slide, or zoom) rather than a simple positional change.

Specs

  • Weight: ~2 KB. Zero dependencies (though a polyfill is used for Firefox animation-timeline support).
  • Performance: Ultra-high. By tying CSS animations directly to the scrollbar position (animation-timeline: --section), the component offloads all the heavy lifting to the browser’s compositor, avoiding janky JavaScript scroll event listeners.
  • Theming / Customization: Easily adaptable. The user can switch between different animation types (blink, horizontal-scroll, zoom-scroll) via radio buttons, which are linked to the component using the CSS :has() pseudo-class.
  • Responsiveness: Native. Uses dynamic viewport height units (100dvh) to ensure sections are always full-screen.
  • Graceful Degradation: [!] Highly experimental. Relies heavily on scroll-snap-type, timeline-scope, and view-timeline. In unsupported browsers (like current versions of Safari), it will behave like a standard, long-scrolling webpage without the snapping or transition effects.

Anatomy

The component uses a clever layering hack to separate the scrolling mechanism from the visual content.

  • HTML (The Skeleton): A <main> wrapper contains five full-height <section> elements. Inside each section is a .content div.
  • CSS (The Skin): This is where the magic happens. The <section> elements are part of the normal document flow and provide the scrollable height. The .content divs, however, are taken out of the flow with position: fixed, stacking them all on top of each other.
  • JS (The Nervous System): Absent. Interaction is handled by the browser’s native scroll and input state mechanisms.

Logic

The core logic is the “Fixed Content Swap”:

.section {
  scroll-snap-align: start;
  view-timeline: --section;
  height: 100dvh;
}
.content {
  position: fixed;
  inset: 0;
  animation: blink ease-in-out both;
  animation-timeline: --section;
}

As the user scrolls, they are actually scrolling through the invisible <section> elements. The scroll-snap-align forces the viewport to lock perfectly to the start of each section. Crucially, each .content div’s animation is tied to its parent <section>’s visibility via animation-timeline: --section. The @keyframes blink animation is designed to be fully visible only at the 50% mark of the timeline (when the section is centered in the viewport). As you scroll from one section to the next, the old content animates out (from 50% to 100%) while the new content animates in (from 0% to 50%), creating a seamless cross-fade.

Feel

Cinematic and immersive. The “melt” effect, created by animating filter: blur() contrast(), makes the content appear to dissolve and reform with each scroll. Because scroll-snap-type: y mandatory hijacks the standard scroll behavior, the experience feels less like a webpage and more like presenting slides in a high-end application like Keynote or a polished mobile app.

Advertisement