spb/svgarden Public
SVGarden — searchable bank of 74 self-contained SVG+CSS animation snippets (svgarden.dev)
HTML 79.2%
Astro 10.3%
JavaScript 6%
CSS 3.7%
Shell 0.8%
1<!--2 ============================================================3 SVGarden — https://www.svgarden.dev4 Author : Simon-Pierre Boucher5 Contact: contact@spboucher.ai6 File : snippets/scroll/scroll-parallax-layers.html7 Desc : Layered SVG mountain scene with scroll-speed parallax depth8 ============================================================9-->10<!--svgarden11title: Scroll parallax layers12slug: scroll-parallax-layers13category: scroll14tags: [scroll, parallax, layers, scene]15difficulty: advanced16techniques: ["animation-timeline: scroll()", "position: sticky", translate-property, "@supports", calc-depth]17how_it_works: >18 The scene is pinned with position: sticky while its parent track is three19 times taller, so scrolling the box scrubs a timeline without the scene ever20 leaving view. All three mountain layers share one translate keyframe, but21 each sets its own --sg-shift (a calc() fraction of the depth variable), so22 the near layer rises five times farther than the far one — unequal travel23 for equal scroll is the entire parallax illusion. Browsers without24 scroll-driven animations skip the @supports block and show the scene as a25 static composition.26support: "Scroll-driven animations need Chrome/Edge 115+; other browsers show the layered scene without parallax motion."27customizable:28 - { var: "--sg-color", label: "Scene color", type: color, default: "#7F77DD" }29 - { var: "--sg-size", label: "Box height", type: range, min: 180, max: 400, default: 260, unit: px }30 - { var: "--sg-depth", label: "Depth", type: range, min: 10, max: 80, default: 40 }31created: 2026-08-1032-->33<div class="sg-scroll-parallax-layers" style="--sg-color:#7F77DD; --sg-size:260px; --sg-depth:40;">34 <div class="sg-scroll-parallax-layers-track">35 <div class="sg-scroll-parallax-layers-sticky">36 <svg viewBox="0 0 200 120" preserveAspectRatio="xMidYMax slice" aria-hidden="true" focusable="false">37 <circle class="sg-scroll-parallax-layers-sun" cx="150" cy="30" r="12" />38 <path class="sg-scroll-parallax-layers-l1"39 d="M0 74 L28 42 L52 70 L84 40 L118 72 L148 46 L176 70 L200 56 V140 H0 Z" />40 <path class="sg-scroll-parallax-layers-l2"41 d="M0 90 C 30 68, 62 98, 102 82 C 142 66, 172 96, 200 84 V140 H0 Z" />42 <path class="sg-scroll-parallax-layers-l3"43 d="M0 104 C 40 88, 82 114, 124 100 C 162 88, 184 108, 200 102 V140 H0 Z" />44 </svg>45 <span class="sg-scroll-parallax-layers-hint">scroll ↓</span>46 </div>47 </div>48</div>49<style>50 .sg-scroll-parallax-layers {51 width: 100%;52 min-width: 220px;53 height: var(--sg-size);54 overflow-y: scroll;55 border: 1px solid rgba(128, 128, 128, 0.3);56 border-radius: 10px;57 }58 .sg-scroll-parallax-layers-track {59 height: calc(var(--sg-size) * 3);60 }61 .sg-scroll-parallax-layers-sticky {62 position: sticky;63 top: 0;64 height: var(--sg-size);65 /* no overflow:hidden here — it would become the nearest scroll container66 and scroll(nearest) would bind to a box that never scrolls */67 }68 .sg-scroll-parallax-layers-sticky svg {69 display: block;70 width: 100%;71 height: 100%;72 }73 .sg-scroll-parallax-layers-hint {74 position: absolute;75 right: 12px;76 top: 8px;77 font-size: 12px;78 opacity: 0.6;79 }80 .sg-scroll-parallax-layers-sun { fill: var(--sg-color); opacity: 0.4; }81 .sg-scroll-parallax-layers-l1 { fill: var(--sg-color); opacity: 0.25; --sg-shift: calc(var(--sg-depth) * -0.2px); }82 .sg-scroll-parallax-layers-l2 { fill: var(--sg-color); opacity: 0.5; --sg-shift: calc(var(--sg-depth) * -0.55px); }83 .sg-scroll-parallax-layers-l3 { fill: var(--sg-color); opacity: 0.9; --sg-shift: calc(var(--sg-depth) * -1px); }84 @supports (animation-timeline: scroll()) {85 .sg-scroll-parallax-layers-l1,86 .sg-scroll-parallax-layers-l2,87 .sg-scroll-parallax-layers-l3 {88 animation: sg-scroll-parallax-layers-rise linear both;89 animation-duration: auto;90 animation-timeline: scroll(nearest);91 }92 }93 @keyframes sg-scroll-parallax-layers-rise {94 from { translate: 0 0; }95 to { translate: 0 var(--sg-shift); }96 }97</style>98