Mountain Parallax Layers
A sticky 340vh landscape where five mountain ridges, clouds, and a rising sun move at independent parallax speeds.
Mountain Parallax Layers the 10th of 12 designs in the 12 CSS Scroll Animations collection. The design pairs CSS styling with a small amount of JavaScript for interactivity. Copy the HTML, CSS and JavaScript panels below into your project — the JS is self-contained, has zero dependencies, and is safe to drop into any framework (React, Vue, Svelte, plain HTML). The design honours prefers-reduced-motion and uses real semantic markup, so it ships accessibility-ready out of the box.
Live preview
This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.
The code
<div class="scroll-space">
<div class="scene">
<div class="sun" id="sun"></div>
<div class="cloud c1" id="cl1"></div>
<div class="cloud c2" id="cl2"></div>
<div class="title" id="title">
<div class="sub">A Parallax Ascent</div>
<h1>Higher<br>Ground</h1>
</div>
<div class="layer l1" id="L1"><svg viewBox="0 0 1200 220" preserveAspectRatio="none"><path d="M0,220 L0,90 L180,30 L360,110 L560,20 L780,120 L980,50 L1200,100 L1200,220 Z"/></svg></div>
<div class="layer l2" id="L2"><svg viewBox="0 0 1200 200" preserveAspectRatio="none"><path d="M0,200 L0,110 L220,50 L420,130 L640,60 L860,140 L1080,70 L1200,120 L1200,200 Z"/></svg></div>
<div class="layer l3" id="L3"><svg viewBox="0 0 1200 180" preserveAspectRatio="none"><path d="M0,180 L0,130 L260,80 L500,150 L720,90 L960,160 L1200,100 L1200,180 Z"/></svg></div>
<div class="layer l4" id="L4"><svg viewBox="0 0 1200 150" preserveAspectRatio="none"><path d="M0,150 L0,120 L300,90 L600,130 L900,95 L1200,125 L1200,150 Z"/></svg></div>
<div class="layer l5" id="L5"><svg viewBox="0 0 1200 110" preserveAspectRatio="none"><path d="M0,110 L0,95 L360,70 L720,100 L1080,72 L1200,90 L1200,110 Z"/></svg></div>
</div>
</div>
<div class="outro">
<p>The summit was never the point — only the layers you crossed to find it.</p>
</div> *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
body{background:#10131C;font-family:'Space Grotesk',sans-serif;overflow-x:hidden}
/* sticky scene */
.scene{
position:sticky;top:0;height:100vh;overflow:hidden;
background:linear-gradient(180deg,#1B2238 0%,#33324A 45%,#7A5C66 78%,#D89A6E 100%);
}
.scroll-space{height:340vh;position:relative}
/* sun */
.sun{
position:absolute;left:50%;top:60%;
width:160px;height:160px;border-radius:50%;
margin-left:-80px;
background:radial-gradient(circle,#FFE7B8,#FBBF6E 55%,rgba(251,191,110,0) 72%);
will-change:transform;
}
/* mountain layers */
.layer{
position:absolute;left:0;right:0;bottom:0;
will-change:transform;
}
.layer svg{display:block;width:100%}
.l1 path{fill:#2A3252}
.l2 path{fill:#3D3F5C}
.l3 path{fill:#5A5063}
.l4 path{fill:#7E5F62}
.l5 path{fill:#A06E5C}
/* drifting cloud band */
.cloud{
position:absolute;width:280px;height:60px;border-radius:60px;
background:rgba(255,255,255,0.08);filter:blur(8px);
will-change:transform;
}
.c1{top:18%;left:10%}
.c2{top:30%;left:55%;width:200px}
/* foreground title */
.title{
position:absolute;left:0;right:0;top:18%;
text-align:center;will-change:transform,opacity;
}
.title .sub{
font-family:'Space Grotesk',sans-serif;font-size:12px;
letter-spacing:0.32em;text-transform:uppercase;
color:rgba(255,255,255,0.5);margin-bottom:0.9rem;
}
.title h1{
font-family:'Cormorant Garamond',serif;font-weight:600;
font-size:clamp(46px,9vw,128px);line-height:0.95;
color:#F4ECE0;letter-spacing:-0.01em;
}
/* after-scene content */
.outro{
background:#D89A6E;padding:7rem 6vw 10rem;text-align:center;
}
.outro p{
font-family:'Cormorant Garamond',serif;font-style:italic;
font-size:clamp(22px,3.4vw,40px);line-height:1.4;
color:#3A2418;max-width:20ch;margin:0 auto;
} const sun=document.getElementById('sun');
const cl1=document.getElementById('cl1');
const cl2=document.getElementById('cl2');
const title=document.getElementById('title');
const L=[1,2,3,4,5].map(i=>document.getElementById('L'+i));
const space=document.querySelector('.scroll-space');
function frame(){
const rect=space.getBoundingClientRect();
const total=space.offsetHeight-window.innerHeight;
const p=Math.min(Math.max(-rect.top/total,0),1); // 0..1
// sun rises
sun.style.transform=`translateY(${ -p*420 }px)`;
// clouds drift sideways
cl1.style.transform=`translateX(${ p*260 }px)`;
cl2.style.transform=`translateX(${ -p*320 }px)`;
// title floats up + fades
title.style.transform=`translateY(${ -p*200 }px)`;
title.style.opacity=String(Math.max(1-p*1.6,0));
// layers rise at different speeds (far slower, near faster)
const speeds=[40,90,150,220,300];
L.forEach((el,i)=>{
el.style.transform=`translateY(${ -p*speeds[i] }px)`;
});
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);