9 CSS 3D Designs 03 / 09

Kinetic Tilt Cards

Three pricing cards (Starter / Pro / Enterprise) that track individual mouse positions via smooth lerp animation loops — each card applies perspective + rotateX + rotateY relative to cursor, with a radial glare gradient following the hotspot.

Best forpricing pages, premium product grids, SaaS upgrade flows, plan-comparison sections.

CSS + JS MIT licensed
Live Demo Open in tab
Open in playground

The code

<section class="cd-tlt" aria-label="Kinetic tilt cards pricing demo">
  <div class="card">
    <div class="tilt-grid">

      <div class="tcard" data-cd-tlt-card data-max="18">
        <div class="tcard-inner">
          <div class="tcard-body">
            <div class="layer-bg" data-cd-tlt-bg></div>
            <div class="layer-mid" data-cd-tlt-mid></div>
            <div class="layer-fg">
              <div class="plan-badge">Starter</div>
              <div class="price-wrap">
                <span class="price-currency">$</span>
                <span class="price-amount">19</span>
                <span class="price-period">/ mo</span>
              </div>
              <p class="plan-desc">Everything you need to get off the ground fast, without compromise.</p>
              <ul class="features">
                <li class="feature">3 active projects</li>
                <li class="feature">10k API calls / mo</li>
                <li class="feature">Community support</li>
                <li class="feature">Basic analytics</li>
                <li class="feature">1 team member</li>
              </ul>
              <button class="plan-cta" type="button">Get Started</button>
            </div>
          </div>
          <div class="tcard-border"></div>
          <div class="glare" data-cd-tlt-glare></div>
        </div>
      </div>

      <div class="tcard featured" data-cd-tlt-card data-max="14" data-raised="1">
        <div class="tcard-inner">
          <div class="tcard-body">
            <div class="layer-bg" data-cd-tlt-bg></div>
            <div class="layer-mid" data-cd-tlt-mid></div>
            <div class="layer-fg">
              <div class="plan-badge">Pro</div>
              <div class="price-wrap">
                <span class="price-currency">$</span>
                <span class="price-amount">79</span>
                <span class="price-period">/ mo</span>
              </div>
              <p class="plan-desc">For growing teams that need power, speed, and room to breathe.</p>
              <ul class="features">
                <li class="feature">Unlimited projects</li>
                <li class="feature">500k API calls / mo</li>
                <li class="feature">Priority support</li>
                <li class="feature">Advanced analytics</li>
                <li class="feature">Up to 12 members</li>
              </ul>
              <button class="plan-cta" type="button">Start Free Trial</button>
            </div>
          </div>
          <div class="tcard-border"></div>
          <div class="glare" data-cd-tlt-glare></div>
        </div>
      </div>

      <div class="tcard" data-cd-tlt-card data-max="18">
        <div class="tcard-inner">
          <div class="tcard-body">
            <div class="layer-bg" data-cd-tlt-bg></div>
            <div class="layer-mid" data-cd-tlt-mid></div>
            <div class="layer-fg">
              <div class="plan-badge">Enterprise</div>
              <div class="price-wrap">
                <span class="price-amount custom">Custom</span>
              </div>
              <p class="plan-desc">Tailored scale, SLA guarantees, and white-glove onboarding for your organization.</p>
              <ul class="features">
                <li class="feature">Unlimited everything</li>
                <li class="feature">Dedicated infrastructure</li>
                <li class="feature">24/7 SLA support</li>
                <li class="feature">SSO + SAML + SOC 2</li>
                <li class="feature">Custom contracts</li>
              </ul>
              <button class="plan-cta" type="button">Contact Sales</button>
            </div>
          </div>
          <div class="tcard-border"></div>
          <div class="glare" data-cd-tlt-glare></div>
        </div>
      </div>

    </div>
  </div>
</section>
/* ─── 03 Kinetic Tilt Cards — pricing tier showcase ────────── */
@import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=Syne+Mono&display=swap');

.cd-tlt {
  --cd-tlt-bg: #07080e;

  position: relative;
  width: 100%;
  min-height: 500px;
  background: var(--cd-tlt-bg);
  font-family: 'Syne', system-ui, sans-serif;
  overflow: hidden;
  box-sizing: border-box;
}

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

.cd-tlt .card {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 36px 24px;
}
.cd-tlt .card::before {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 55% 45% at 20% 30%, rgba(20,80,180,0.09) 0%, transparent 70%),
    radial-gradient(ellipse 50% 40% at 80% 70%, rgba(80,20,160,0.09) 0%, transparent 70%),
    radial-gradient(ellipse 40% 30% at 50% 50%, rgba(0,200,180,0.04) 0%, transparent 70%);
  pointer-events: none;
}

.cd-tlt .tilt-grid {
  display: flex;
  gap: 28px;
  align-items: center;
  perspective: 2000px;
  position: relative;
  z-index: 1;
  flex-wrap: wrap;
  justify-content: center;
}

.cd-tlt .tcard {
  width: 244px;
  height: 340px;
  position: relative;
  cursor: pointer;
  transform-style: preserve-3d;
  transition: transform 0.08s linear;
  border-radius: 22px;
}
.cd-tlt .tcard-inner {
  position: absolute;
  inset: 0;
  border-radius: 22px;
  overflow: hidden;
  transform-style: preserve-3d;
}
.cd-tlt .tcard-body {
  position: absolute;
  inset: 0;
  border-radius: 22px;
  padding: 26px 22px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  overflow: hidden;
}
.cd-tlt .glare {
  position: absolute;
  inset: 0;
  border-radius: 22px;
  pointer-events: none;
  z-index: 10;
  opacity: 0;
  transition: opacity 0.3s;
}
.cd-tlt .tcard:hover .glare { opacity: 1; }
.cd-tlt .tcard-border {
  position: absolute;
  inset: 0;
  border-radius: 22px;
  border: 1px solid rgba(255,255,255,0);
  pointer-events: none;
  z-index: 5;
  transition: border-color 0.3s;
}
.cd-tlt .tcard:hover .tcard-border { border-color: rgba(255,255,255,0.1); }

.cd-tlt .layer-bg { position: absolute; inset: 0; border-radius: 22px; transition: transform 0.08s linear; }
.cd-tlt .layer-mid { position: absolute; pointer-events: none; transition: transform 0.08s linear; }
.cd-tlt .layer-fg { position: relative; z-index: 3; }

.cd-tlt .plan-badge {
  display: inline-block;
  padding: 5px 12px;
  border-radius: 4px;
  font-family: 'Syne Mono', monospace;
  font-size: 10px;
  letter-spacing: 2px;
  text-transform: uppercase;
  margin-bottom: 14px;
  width: fit-content;
}
.cd-tlt .price-wrap { margin-bottom: 18px; }
.cd-tlt .price-currency {
  font-size: 13px;
  font-weight: 600;
  vertical-align: top;
  line-height: 2.4;
}
.cd-tlt .price-amount {
  font-size: 46px;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -2px;
}
.cd-tlt .price-amount.custom {
  font-size: 32px;
  letter-spacing: -1px;
}
.cd-tlt .price-period {
  font-size: 12px;
  font-weight: 400;
  opacity: 0.55;
  margin-left: 2px;
}
.cd-tlt .plan-desc {
  font-size: 12px;
  font-weight: 400;
  opacity: 0.65;
  line-height: 1.6;
  margin-bottom: 20px;
}
.cd-tlt .features {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1;
}
.cd-tlt .feature {
  font-size: 11px;
  font-weight: 500;
  display: flex;
  align-items: center;
  gap: 8px;
  opacity: 0.8;
}
.cd-tlt .feature::before {
  content: '◆';
  font-size: 9px;
  opacity: 0.4;
}
.cd-tlt .plan-cta {
  display: block;
  width: 100%;
  padding: 12px;
  border-radius: 10px;
  font-family: 'Syne', sans-serif;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  cursor: pointer;
  border: none;
  margin-top: 18px;
  transition: transform 0.15s, filter 0.15s;
}
.cd-tlt .plan-cta:hover { filter: brightness(1.12); transform: translateY(-1px); }
.cd-tlt .plan-cta:active { transform: translateY(1px); }

/* Card 1 — Starter / steel */
.cd-tlt .tcard:nth-child(1) .tcard-body { background: linear-gradient(145deg, #111420 0%, #0d1018 55%, #131722 100%); }
.cd-tlt .tcard:nth-child(1) .layer-bg { background: radial-gradient(ellipse 80% 60% at 30% 20%, rgba(40,100,200,0.12) 0%, transparent 70%); }
.cd-tlt .tcard:nth-child(1) .plan-badge { background: rgba(80,140,255,0.12); color: #5090ff; }
.cd-tlt .tcard:nth-child(1) .price-currency,
.cd-tlt .tcard:nth-child(1) .price-amount,
.cd-tlt .tcard:nth-child(1) .price-period { color: #c8d8ff; }
.cd-tlt .tcard:nth-child(1) .plan-desc { color: rgba(180,200,255,0.65); }
.cd-tlt .tcard:nth-child(1) .feature { color: rgba(180,200,255,0.75); }
.cd-tlt .tcard:nth-child(1) .plan-cta { background: rgba(80,140,255,0.15); color: #7090ff; border: 1px solid rgba(80,140,255,0.3); }
.cd-tlt .tcard:nth-child(1) .layer-mid {
  width: 180px; height: 180px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(60,120,255,0.06) 0%, transparent 70%);
  border: 1px solid rgba(60,120,255,0.08);
  top: -40px; right: -40px;
}

/* Card 2 — Pro / featured */
.cd-tlt .tcard.featured .tcard-body { background: linear-gradient(145deg, #0e1f0e 0%, #0a1808 55%, #101f10 100%); }
.cd-tlt .tcard.featured .layer-bg { background: radial-gradient(ellipse 80% 60% at 50% 25%, rgba(0,200,100,0.14) 0%, transparent 70%); }
.cd-tlt .tcard.featured .plan-badge { background: rgba(0,200,100,0.12); color: #00d468; }
.cd-tlt .tcard.featured .price-currency,
.cd-tlt .tcard.featured .price-amount,
.cd-tlt .tcard.featured .price-period { color: #c0ffd8; }
.cd-tlt .tcard.featured .plan-desc { color: rgba(160,240,190,0.65); }
.cd-tlt .tcard.featured .feature { color: rgba(160,240,190,0.75); }
.cd-tlt .tcard.featured .plan-cta { background: #00c860; color: #001a0a; border: none; font-weight: 800; }
.cd-tlt .tcard.featured {
  transform: translateY(-12px);
  box-shadow: 0 40px 80px rgba(0,200,80,0.08), 0 20px 40px rgba(0,0,0,0.6);
}
.cd-tlt .tcard.featured::after {
  content: 'MOST POPULAR';
  position: absolute;
  top: -12px;
  left: 50%;
  transform: translateX(-50%);
  background: #00c860;
  color: #001a0a;
  font-family: 'Syne', sans-serif;
  font-size: 8px;
  font-weight: 800;
  letter-spacing: 2.5px;
  padding: 4px 12px;
  border-radius: 3px;
  z-index: 20;
  white-space: nowrap;
}
.cd-tlt .tcard.featured .layer-mid {
  width: 220px; height: 220px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(0,200,100,0.07) 0%, transparent 70%);
  border: 1px solid rgba(0,200,100,0.1);
  top: -60px; right: -60px;
}

/* Card 3 — Enterprise */
.cd-tlt .tcard:nth-child(3) .tcard-body { background: linear-gradient(145deg, #1a0e1a 0%, #120a14 55%, #1c1020 100%); }
.cd-tlt .tcard:nth-child(3) .layer-bg { background: radial-gradient(ellipse 80% 60% at 70% 30%, rgba(180,80,255,0.12) 0%, transparent 70%); }
.cd-tlt .tcard:nth-child(3) .plan-badge { background: rgba(180,80,255,0.12); color: #c060ff; }
.cd-tlt .tcard:nth-child(3) .price-currency,
.cd-tlt .tcard:nth-child(3) .price-amount,
.cd-tlt .tcard:nth-child(3) .price-period { color: #ecd8ff; }
.cd-tlt .tcard:nth-child(3) .plan-desc { color: rgba(220,180,255,0.65); }
.cd-tlt .tcard:nth-child(3) .feature { color: rgba(220,180,255,0.75); }
.cd-tlt .tcard:nth-child(3) .plan-cta { background: rgba(180,80,255,0.15); color: #c080ff; border: 1px solid rgba(180,80,255,0.3); }
.cd-tlt .tcard:nth-child(3) .layer-mid {
  width: 160px; height: 160px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(180,80,255,0.06) 0%, transparent 70%);
  border: 1px solid rgba(180,80,255,0.08);
  bottom: 10px; left: -30px;
}

@media (max-width: 720px) {
  .cd-tlt { min-height: 1100px; }
  .cd-tlt .tcard.featured { transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .cd-tlt .tcard,
  .cd-tlt .layer-bg,
  .cd-tlt .layer-mid { transition: none !important; transform: none !important; }
}
(() => {
  const root = document.querySelector('.cd-tlt');
  if (!root) return;
  const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  if (prefersReduced) return;

  root.querySelectorAll('[data-cd-tlt-card]').forEach(card => {
    const maxTilt = parseFloat(card.dataset.max) || 16;
    const glare = card.querySelector('[data-cd-tlt-glare]');
    const layerBg = card.querySelector('[data-cd-tlt-bg]');
    const layerMid = card.querySelector('[data-cd-tlt-mid]');
    const raised = card.dataset.raised === '1';

    let animFrame = null;
    let targetRx = 0, targetRy = 0;
    let currentRx = 0, currentRy = 0;
    let active = false;

    function lerp(a, b, t) { return a + (b - a) * t; }

    function tick() {
      if (!active) {
        currentRx = lerp(currentRx, 0, 0.08);
        currentRy = lerp(currentRy, 0, 0.08);
      } else {
        currentRx = lerp(currentRx, targetRx, 0.12);
        currentRy = lerp(currentRy, targetRy, 0.12);
      }
      const baseY = raised ? -12 : 0;
      card.style.transform = `perspective(1000px) rotateX(${currentRx}deg) rotateY(${currentRy}deg) translateY(${baseY}px)`;
      if (layerBg) layerBg.style.transform = `translateX(${currentRy * 1.5}px) translateY(${-currentRx * 1.5}px)`;
      if (layerMid) layerMid.style.transform = `translateX(${currentRy * 3}px) translateY(${-currentRx * 3}px)`;

      if (active || Math.abs(currentRx) > 0.05 || Math.abs(currentRy) > 0.05) {
        animFrame = requestAnimationFrame(tick);
      } else {
        animFrame = null;
      }
    }

    card.addEventListener('mouseenter', () => {
      active = true;
      if (!animFrame) tick();
    });
    card.addEventListener('mousemove', e => {
      const rect = card.getBoundingClientRect();
      const cx = rect.left + rect.width / 2;
      const cy = rect.top + rect.height / 2;
      const dx = (e.clientX - cx) / (rect.width / 2);
      const dy = (e.clientY - cy) / (rect.height / 2);
      targetRy = dx * maxTilt;
      targetRx = -dy * maxTilt;
      const glareX = ((e.clientX - rect.left) / rect.width) * 100;
      const glareY = ((e.clientY - rect.top) / rect.height) * 100;
      if (glare) glare.style.background = `radial-gradient(circle at ${glareX}% ${glareY}%, rgba(255,255,255,0.18) 0%, transparent 55%)`;
    });
    card.addEventListener('mouseleave', () => {
      active = false;
      targetRx = 0;
      targetRy = 0;
      if (glare) glare.style.background = 'none';
      if (!animFrame) tick();
    });
  });
})();

Search CodeFronts

Loading…