JSFiddle - React, Tailwind, and code Playground

https://old.reddit.com/r/webdev/comments/9w39r4/how_would_something_like_the_card_scroll_even_be/

by alexb

HTML

<div class="cards">
  <div class="card card-gray"></div>
  <div class="card card-blue"></div>
  <div class="card card-pink"></div>
</div>

<input type="range" value="0" max="1000">

CSS

.cards {
  height: 300px;
  padding: 50px;
  perspective: 800px;
}

.card {
  border-radius: 25px;
  height: 250px;
  width: 400px;
  position: absolute;
}

.card-gray {
  background-image: radial-gradient(farthest-corner at top left, #808080, #404040);
}
.card-blue {
  background-image: radial-gradient(farthest-corner at top left, hsla(210, 74%, 50%), hsla(210, 74%, 25%));
}
.card-pink {
  background-image: radial-gradient(farthest-corner at top left, hsla(350, 95%, 90%), hsla(350, 100%, 75%));
}

JavaScript

const range = document.querySelector('[type=range]');

const allCards = document.querySelectorAll('.card');
const offscreenCards = [...allCards].slice(0, allCards.length - 1);
const mainCard = allCards[allCards.length - 1];

function update() {
	const progress = range.value / range.max;
  
	allCards.forEach(card => {
  	card.style.transform = `rotateX(${-40 * progress}deg)`;
  });
  
  for (let i = 0; i < offscreenCards.length; i++) {
  	const s = offscreenCards.length;
  	const p = clamp(0, 1, progress * s - (s - i - 1));
  	offscreenCards[i].style.top = `${lerp(-500 + 100/s*i, 50 + 100/s*i, p)}px`;
    offscreenCards[i].style.opacity = p;
  }
  mainCard.style.top = `${lerp(50, 150, clamp(0, 1, progress * offscreenCards.length))}px`;
}

range.addEventListener('input', update)
update();

function lerp(min, max, val) {
		return min + (max - min) * val;
}

function clamp(min, max, val) {
	return Math.min(max, Math.max(min, val));
}