JSFiddle - React, Tailwind, and code Playground

by Jordan Sayner

HTML

<div class="card-wrapper">
  <div class="card" data-scroll-offset="10">
    <h2>Card Title 1</h2>
    <p>Card description...</p>
  </div>
  <div class="card" data-scroll-offset="10">
    <h2>Card Title 2</h2>
    <p>More information...</p>
  </div>
  <div class="card" data-scroll-offset="10">
    <h2>Card Title 3</h2>
    <p>More information...</p>
  </div>
  <div class="card" data-scroll-offset="10">
    <h2>Card Title 3</h2>
    <p>More information...</p>
  </div>
  <div class="card" data-scroll-offset="10">
    <h2>Card Title 3</h2>
    <p>More information...</p>
  </div>
  <div class="card" data-scroll-offset="10">
    <h2>Card Title 3</h2>
    <p>More information...</p>
  </div>
</div>

CSS

.card-wrapper {
  display: flex;
  flex-direction: column;
  margin: 0 auto;
  position: relative;
}

.card {
  background-color: #fff;
  border-radius: 5px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
  padding: 20px;
  margin-bottom: 20px; /* Spacing between cards */
  position: sticky;
  top: 0;
  transition: transform 0.2s ease-in-out, filter 0.2s ease-in-out;
  aspect-ratio: 1220 / 766;
  z-index: 1; /* Ensure stacking */
}

.card.scaled {
  transform: scale(0.8);
  filter: blur(4px);
}

JavaScript

document.addEventListener('scroll', () => {
  const cards = document.querySelectorAll('.card');
  cards.forEach((card, index) => {
    const rect = card.getBoundingClientRect();
    const scrollOffset = parseInt(card.dataset.scrollOffset, 10) || 100;

    if (rect.top <= scrollOffset) {
      card.classList.add('scaled');
      // Adjust z-index to keep the stacking order
      card.style.zIndex = cards.length - index;
    } else {
      card.classList.remove('scaled');
      card.style.zIndex = 1;
    }
  });
});