Back to CSS Stacked Cards Tilt on Hover CSS + JS
Share
HTML
<div class="scd-tilt">
  <div class="scd-tilt__stage">
    <div class="scd-tilt__card scd-tilt__card--c2"><div class="scd-tilt__glare"></div><div class="scd-tilt__content"><h3>Beneath</h3><p>The supporting layer</p></div></div>
    <div class="scd-tilt__card scd-tilt__card--c1" data-scd-tilt="stage"><div class="scd-tilt__glare" data-scd-tilt="glare"></div><div class="scd-tilt__content"><h3>Tilt me</h3><p>Move your cursor across</p></div></div>
  </div>
</div>
CSS
@import url('https://fonts.googleapis.com/css2?family=Familjen+Grotesk:wght@600;700&display=swap');

.scd-tilt, .scd-tilt *, .scd-tilt *::before, .scd-tilt *::after { box-sizing: border-box; margin: 0; padding: 0; }

.scd-tilt {
  min-height: 460px;
  display: grid;
  place-items: center;
  background: linear-gradient(135deg,#141e30,#243b55);
  font-family: 'Familjen Grotesk', sans-serif;
}
.scd-tilt__stage {
  position: relative;
  width: 250px; height: 340px;
  perspective: 800px;
}
.scd-tilt__card {
  position: absolute; inset: 0;
  border-radius: 22px;
  transform-style: preserve-3d;
  transition: transform .15s ease-out, box-shadow .3s;
  box-shadow: 0 20px 50px rgba(0,0,0,.5);
  overflow: hidden;
}
.scd-tilt__card::before {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(150deg,#fc466b,#3f5efb);
}
.scd-tilt__glare {
  position: absolute; inset: 0;
  background: radial-gradient(circle at var(--gx,50%) var(--gy,50%),rgba(255,255,255,.45),transparent 55%);
  pointer-events: none;
}
.scd-tilt__content {
  position: absolute; inset: 0;
  padding: 28px;
  display: flex; flex-direction: column; justify-content: flex-end;
  color: #fff;
  transform: translateZ(40px);
}
.scd-tilt__card h3 { font-size: 1.9rem; font-weight: 700; }
.scd-tilt__card p { opacity: .85; }
.scd-tilt__card--c2 { transform: translate(10px,10px) scale(.96); z-index: 1; }
.scd-tilt__card--c2::before { background: linear-gradient(150deg,#3f5efb,#11998e); }
.scd-tilt__card--c1 { z-index: 2; }

@media (prefers-reduced-motion: reduce) {
  .scd-tilt__card { transition: none !important; }
}
JS
(() => {
  const root = document.querySelector('.scd-tilt');
  if (!root) return;
  const card = root.querySelector('[data-scd-tilt="stage"]');
  const glare = root.querySelector('[data-scd-tilt="glare"]');
  if (!card || !glare) return;
  const stack = card.parentElement;

  stack.addEventListener('mousemove', (e) => {
    const r = card.getBoundingClientRect();
    const x = (e.clientX - r.left) / r.width;
    const y = (e.clientY - r.top) / r.height;
    card.style.transform = `rotateY(${(x - 0.5) * 24}deg) rotateX(${(0.5 - y) * 24}deg)`;
    glare.style.setProperty('--gx', x * 100 + '%');
    glare.style.setProperty('--gy', y * 100 + '%');
  });
  stack.addEventListener('mouseleave', () => {
    card.style.transform = '';
  });
})();