Layered landscape
Five mountain ridges slide at different speeds to build depth out of flat shapes.
Scattered cards drift at their own speeds, so the page feels like a stack of glass sheets.
Cards are absolutely placed through a tall field. Each one gets a random speed between −0.3 and 0.5; small, blurred cards get slow speeds to read as far away, large sharp ones fast speeds to read as close.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Floating depth cards</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; }
body { font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; -webkit-font-smoothing: antialiased; }
body { background: #0f2027; color: #fff; overflow-x: hidden; }
.field { position: relative; height: 300vh; }
.field h1 { position: sticky; top: 40vh; text-align: center; margin: 0; font-size: 3rem; z-index: 5; mix-blend-mode: difference; }
.f { position: absolute; border-radius: 14px; will-change: transform; }
</style>
</head>
<body>
<div class="field" id="field"><h1>Depth field</h1></div>
<script>
const field = document.getElementById('field');
const palette = ['#2ec4b6', '#ff9f1c', '#e71d36', '#cbf3f0', '#ffbf69'];
const items = [];
for (let i = 0; i < 28; i++) {
const d = Math.random();
const el = document.createElement('div');
el.className = 'f';
const size = 40 + d * 160;
el.style.cssText = 'width:' + size + 'px;height:' + (size * 0.7) + 'px;left:' + (Math.random() * 90) + '%;top:' + (Math.random() * 280) + 'vh;background:' + palette[i % 5] + ';filter:blur(' + ((1 - d) * 4).toFixed(1) + 'px);opacity:' + (0.35 + d * 0.65).toFixed(2) + ';z-index:' + Math.round(d * 10);
field.appendChild(el);
items.push({ el, speed: -0.3 + d * 0.8 });
}
function update() {
items.forEach(o => { o.el.style.transform = 'translate3d(0,' + (-scrollY * o.speed) + 'px,0)'; });
}
addEventListener('scroll', update, { passive: true });
update();
</script>
</body>
</html>