JSFiddle - React, Tailwind, and code Playground
by Jordan Sayner
HTML
<div class="card-wrapper">
<div class="card" data-scroll-offset="10">
<h2>Card Title</h2>
<p>Card description...</p>
</div>
<div class="card" data-scroll-offset="10">
<h2>Another title</h2>
<p>More information...</p>
</div>
<div class="card" data-scroll-offset="10">
<h2>Another title</h2>
<p>More information...</p>
</div>
<div class="card" data-scroll-offset="10">
<h2>Another title</h2>
<p>More information...</p>
</div>
<div class="card" data-scroll-offset="10">
<h2>Another title</h2>
<p>More information...</p>
</div>
<div class="card" data-scroll-offset="10">
<h2>Another title</h2>
<p>More information...</p>
</div>
<div class="card" data-scroll-offset="10">
<h2>Another title</h2>
<p>More information...</p>
</div>
<div class="card" data-scroll-offset="10">
<h2>Another title</h2>
<p>More information...</p>
</div>
<div class="card" data-scroll-offset="10">
<h2>Another title</h2>
<p>More information...</p>
</div>
<div class="card" data-scroll-offset="10">
<h2>Another title</h2>
<p>More information...</p>
</div>
</div>
CSS
.card-wrapper {
display: flex;
flex-direction: column;
margin: 0 auto;
max-width: 700px; /* Adjust as needed */
}
.card {
background-color: #fff;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
padding: 20px;
margin-bottom: 20px; /* Adjust spacing between cards */
position: sticky;
top: var(--card-offset, 0);
transition: transform 0.2s ease-in-out, filter 0.2s ease-in-out;
aspect-ratio: 1220 / 766;
}
.card:first-child {
margin-top: 0; /* Remove top margin for first card */
}
.card--small {
transform: scale(0.8);
filter: blur(4px);
}
JavaScript
const cards = document.querySelectorAll('.card');
window.addEventListener('scroll', () => {
cards.forEach(card => {
const cardTop = card.getBoundingClientRect().top;
const cardHeight = card.offsetHeight;
const viewportHeight = window.innerHeight;
const isFullyScrolled = cardTop + cardHeight <= viewportHeight;
if (isFullyScrolled) {
card.style.transform = 'scale(0.8)';
card.style.filter = 'blur(4px)';
} else {
const scrollOffset = card.dataset.scrollOffset;
const scrollProgress = Math.max(0, Math.min(1, (scrollOffset - cardTop) / scrollOffset));
const scale = 1 - scrollProgress * 0.2;
const blur = scrollProgress * 4;
card.style.transform = `scale(${Math.max(0.8, scale)})`;
card.style.filter = `blur(${Math.min(4, blur)}px)`;
}
});
});