HTML Copy
<div class="scd-swipe">
<div class="scd-swipe__stage" data-scd-swipe="stage">
<div class="scd-swipe__card scd-swipe__card--b4"><h3>Maya</h3><p>Loves slow mornings & film cameras</p></div>
<div class="scd-swipe__card scd-swipe__card--b3"><h3>Theo</h3><p>Trail runner, terrible cook</p></div>
<div class="scd-swipe__card scd-swipe__card--b2"><h3>Lena</h3><p>Vinyl collector since '09</p></div>
<div class="scd-swipe__card scd-swipe__card--b1"><h3>Drag me</h3><p>Swipe left or right →</p></div>
<div class="scd-swipe__hint">drag the top card</div>
</div>
</div> CSS Copy
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@600;800&display=swap');
.scd-swipe, .scd-swipe *, .scd-swipe *::before, .scd-swipe *::after { box-sizing: border-box; margin: 0; padding: 0; }
.scd-swipe {
min-height: 460px;
display: grid;
place-items: center;
background: linear-gradient(160deg,#fdeff9,#ec38bc 120%);
font-family: 'Plus Jakarta Sans', sans-serif;
}
.scd-swipe__stage { position: relative; width: 260px; height: 360px; }
.scd-swipe__card {
position: absolute; inset: 0;
border-radius: 26px;
color: #fff;
box-shadow: 0 20px 44px rgba(120,30,90,.35);
display: flex; flex-direction: column; justify-content: flex-end;
padding: 28px;
cursor: grab; user-select: none;
transition: transform .4s cubic-bezier(.3,.9,.3,1);
}
.scd-swipe__card.is-dragging { transition: none; cursor: grabbing; }
.scd-swipe__card h3 { font-size: 1.9rem; font-weight: 800; }
.scd-swipe__card p { opacity: .9; }
.scd-swipe__card--b1 { background: linear-gradient(150deg,#fa709a,#fee140); }
.scd-swipe__card--b2 { background: linear-gradient(150deg,#a18cd1,#fbc2eb); color: #5a2a6a; }
.scd-swipe__card--b3 { background: linear-gradient(150deg,#43cea2,#185a9d); }
.scd-swipe__card--b4 { background: linear-gradient(150deg,#ff6e7f,#bfe9ff); color: #3a3a5a; }
.scd-swipe__hint {
position: absolute; bottom: -36px; width: 100%;
text-align: center; color: #fff; opacity: .8; font-size: .85rem;
}
@media (prefers-reduced-motion: reduce) {
.scd-swipe__card { transition: none !important; }
} JS Copy
(() => {
const root = document.querySelector('.scd-swipe');
if (!root) return;
const deck = root.querySelector('[data-scd-swipe="stage"]');
if (!deck) return;
function layout() {
const cards = [...deck.querySelectorAll('.scd-swipe__card')];
cards.forEach((c, i) => {
const d = cards.length - 1 - i;
c.style.transform = `translateY(${d * -10}px) scale(${1 - d * 0.04})`;
c.style.zIndex = i;
});
}
layout();
function bind() {
const top = [...deck.querySelectorAll('.scd-swipe__card')].pop();
if (!top) return;
let sx = 0, dx = 0, drag = false;
const down = (e) => {
drag = true;
sx = (e.touches ? e.touches[0].clientX : e.clientX);
top.classList.add('is-dragging');
};
const move = (e) => {
if (!drag) return;
dx = (e.touches ? e.touches[0].clientX : e.clientX) - sx;
top.style.transform = `translateX(${dx}px) rotate(${dx * 0.06}deg)`;
};
const up = () => {
if (!drag) return;
drag = false;
top.classList.remove('is-dragging');
if (Math.abs(dx) > 110) {
top.style.transform = `translateX(${dx > 0 ? 600 : -600}px) rotate(${dx > 0 ? 40 : -40}deg)`;
top.style.opacity = 0;
setTimeout(() => { top.remove(); layout(); bind(); }, 300);
} else {
top.style.transform = '';
layout();
}
dx = 0;
};
top.addEventListener('mousedown', down);
root.addEventListener('mousemove', move);
root.addEventListener('mouseup', up);
root.addEventListener('mouseleave', up);
top.addEventListener('touchstart', down);
root.addEventListener('touchmove', move);
root.addEventListener('touchend', up);
}
bind();
})();