21 CSS Pricing Sections14 / 21

Pure CSSMIT licensed

3D Tilt Cards

Three pricing cards in 3D perspective, tilting back when not hovered and snapping forward on hover. Purple → cyan gradient surface, layered shadow stack giving real depth. perspective() + rotateY(), no JS.

Published

Live Demo
Try it

The code

<section class="ps-14" aria-label="Pricing">
  <header class="ps-14__head">
    <h2>Pricing with depth.</h2>
    <p>Hover to bring a plan forward.</p>
  </header>
  <div class="ps-14__stage">
    <article class="ps-14__card ps-14__l">
      <h3>Lite</h3>
      <div class="ps-14__price"><em>$</em>9<span>/mo</span></div>
      <ul>
        <li>Personal projects</li>
        <li>5 GB storage</li>
        <li>Community support</li>
      </ul>
      <a class="ps-14__cta" href="#lite">Pick Lite</a>
    </article>
    <article class="ps-14__card ps-14__c">
      <span class="ps-14__pill">Best fit</span>
      <h3>Plus</h3>
      <div class="ps-14__price"><em>$</em>29<span>/mo</span></div>
      <ul>
        <li>Unlimited projects</li>
        <li>50 GB storage</li>
        <li>Priority support</li>
        <li>Custom domains</li>
      </ul>
      <a class="ps-14__cta ps-14__cta-pri" href="#plus">Pick Plus</a>
    </article>
    <article class="ps-14__card ps-14__r">
      <h3>Power</h3>
      <div class="ps-14__price"><em>$</em>79<span>/mo</span></div>
      <ul>
        <li>Everything in Plus</li>
        <li>SSO + audit logs</li>
        <li>Dedicated CSM</li>
      </ul>
      <a class="ps-14__cta" href="#power">Pick Power</a>
    </article>
  </div>
</section>
.ps-14 {
  padding: clamp(28px, 5vw, 56px) clamp(20px, 4vw, 40px);
  width: 100%;
  min-height: 100vh;
  background: radial-gradient(60% 60% at 50% 0%, rgba(124,58,237,0.18), transparent 60%), radial-gradient(60% 60% at 50% 100%, rgba(34,211,238,0.14), transparent 60%), #0d0a1a;
  color: #e9e6ff;
  font-family: 'Inter', system-ui, sans-serif;
  perspective: 1400px;
}

.ps-14__head {
  text-align: center;
  margin-bottom: clamp(24px, 4vw, 40px);
}

.ps-14__head h2 {
  font-size: clamp(24px, 3.6vw, 36px);
  font-weight: 700;
  letter-spacing: -0.02em;
  margin: 0 0 8px;
  color: #fff;
}

.ps-14__head p {
  color: #a8a0c8;
  margin: 0;
  font-size: 14px;
}

.ps-14__stage {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
  max-width: 980px;
  margin: 0 auto;
  transform-style: preserve-3d;
  align-items: center;
}

.ps-14__card {
  position: relative;
  background: linear-gradient(160deg, rgba(124,58,237,0.18), rgba(34,211,238,0.10));
  border: 1px solid rgba(167,139,250,0.25);
  border-radius: 16px;
  padding: 26px 22px 22px;
  display: flex;
  flex-direction: column;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  transform-origin: center;
  transition: transform 0.4s cubic-bezier(.22,1,.36,1), box-shadow 0.4s;
  box-shadow: 0 10px 30px rgba(13,10,26,0.5);
}

.ps-14__l {
  transform: rotateY(14deg) translateZ(-30px);
}

.ps-14__c {
  transform: translateZ(0);
  z-index: 2;
}

.ps-14__r {
  transform: rotateY(-14deg) translateZ(-30px);
}

.ps-14__card:hover {
  transform: rotateY(0) translateZ(20px) translateY(-4px);
  box-shadow: 0 24px 60px rgba(124,58,237,0.4);
  z-index: 3;
}

.ps-14__c {
  background: linear-gradient(160deg, rgba(124,58,237,0.32), rgba(34,211,238,0.18));
  border-color: rgba(34,211,238,0.45);
}

.ps-14__pill {
  position: absolute;
  top: -12px;
  left: 50%;
  transform: translateX(-50%);
  background: linear-gradient(135deg, #a78bfa, #22d3ee);
  color: #0d0a1a;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 5px 12px;
  border-radius: 999px;
}

.ps-14__card h3 {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin: 0 0 12px;
  color: #c4b5fd;
}

.ps-14__c h3 {
  color: #67e8f9;
}

.ps-14__price {
  display: flex;
  align-items: baseline;
  gap: 4px;
  font-size: 38px;
  font-weight: 800;
  letter-spacing: -0.03em;
  margin-bottom: 18px;
  color: #fff;
}

.ps-14__price em {
  font-size: 0.5em;
  font-style: normal;
  padding-right: 2px;
  opacity: 0.7;
}

.ps-14__price span {
  font-size: 0.32em;
  font-weight: 500;
  opacity: 0.65;
  margin-left: 6px;
}

.ps-14__card ul {
  list-style: none;
  padding: 0;
  margin: 0 0 22px;
  flex: 1;
  font-size: 13px;
  line-height: 1.95;
  color: #c4bde0;
}

.ps-14__card ul li::before {
  content: '◆';
  color: #a78bfa;
  display: inline-block;
  width: 20px;
  font-size: 9px;
}

.ps-14__c ul li::before {
  color: #22d3ee;
}

.ps-14__cta {
  display: block;
  text-align: center;
  padding: 11px 16px;
  border-radius: 9px;
  background: rgba(167,139,250,0.12);
  color: #c4b5fd;
  text-decoration: none;
  font-size: 13.5px;
  font-weight: 600;
  border: 1px solid rgba(167,139,250,0.3);
  transition: background 0.18s, color 0.18s;
}

.ps-14__cta-pri {
  background: linear-gradient(135deg, #a78bfa, #22d3ee);
  color: #0d0a1a;
  border-color: transparent;
}

.ps-14__cta:hover {
  background: rgba(167,139,250,0.28);
  color: #fff;
}

.ps-14__cta:focus-visible {
  outline: 2px solid #22d3ee;
  outline-offset: 3px;
}

@media (max-width: 820px) {
  .ps-14__stage {
    grid-template-columns: 1fr;
    max-width: 380px;
    perspective: none;
  }

  .ps-14__l,
    .ps-14__c,
    .ps-14__r {
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ps-14__card {
    transition: none;
  }

  .ps-14__l,
    .ps-14__c,
    .ps-14__r {
    transform: none;
  }

  .ps-14__card:hover {
    transform: none;
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Pricing Section from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: 3D Tilt Cards
Source: https://codefronts.com/layouts/css-pricing-sections/3d-tilt-cards/

Three pricing cards in 3D perspective, tilting back when not hovered and snapping forward on hover. Purple → cyan gradient surface, layered shadow stack giving real depth. perspective() + rotateY(), no JS.
## HTML
```html
<section class="ps-14" aria-label="Pricing">
  <header class="ps-14__head">
    <h2>Pricing with depth.</h2>
    <p>Hover to bring a plan forward.</p>
  </header>
  <div class="ps-14__stage">
    <article class="ps-14__card ps-14__l">
      <h3>Lite</h3>
      <div class="ps-14__price"><em>$</em>9<span>/mo</span></div>
      <ul>
        <li>Personal projects</li>
        <li>5 GB storage</li>
        <li>Community support</li>
      </ul>
      <a class="ps-14__cta" href="#lite">Pick Lite</a>
    </article>
    <article class="ps-14__card ps-14__c">
      <span class="ps-14__pill">Best fit</span>
      <h3>Plus</h3>
      <div class="ps-14__price"><em>$</em>29<span>/mo</span></div>
      <ul>
        <li>Unlimited projects</li>
        <li>50 GB storage</li>
        <li>Priority support</li>
        <li>Custom domains</li>
      </ul>
      <a class="ps-14__cta ps-14__cta-pri" href="#plus">Pick Plus</a>
    </article>
    <article class="ps-14__card ps-14__r">
      <h3>Power</h3>
      <div class="ps-14__price"><em>$</em>79<span>/mo</span></div>
      <ul>
        <li>Everything in Plus</li>
        <li>SSO + audit logs</li>
        <li>Dedicated CSM</li>
      </ul>
      <a class="ps-14__cta" href="#power">Pick Power</a>
    </article>
  </div>
</section>
```
## CSS
```css
.ps-14 {
  padding: clamp(28px, 5vw, 56px) clamp(20px, 4vw, 40px);
  width: 100%;
  min-height: 100vh;
  background: radial-gradient(60% 60% at 50% 0%, rgba(124,58,237,0.18), transparent 60%), radial-gradient(60% 60% at 50% 100%, rgba(34,211,238,0.14), transparent 60%), #0d0a1a;
  color: #e9e6ff;
  font-family: 'Inter', system-ui, sans-serif;
  perspective: 1400px;
}

.ps-14__head {
  text-align: center;
  margin-bottom: clamp(24px, 4vw, 40px);
}

.ps-14__head h2 {
  font-size: clamp(24px, 3.6vw, 36px);
  font-weight: 700;
  letter-spacing: -0.02em;
  margin: 0 0 8px;
  color: #fff;
}

.ps-14__head p {
  color: #a8a0c8;
  margin: 0;
  font-size: 14px;
}

.ps-14__stage {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
  max-width: 980px;
  margin: 0 auto;
  transform-style: preserve-3d;
  align-items: center;
}

.ps-14__card {
  position: relative;
  background: linear-gradient(160deg, rgba(124,58,237,0.18), rgba(34,211,238,0.10));
  border: 1px solid rgba(167,139,250,0.25);
  border-radius: 16px;
  padding: 26px 22px 22px;
  display: flex;
  flex-direction: column;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  transform-origin: center;
  transition: transform 0.4s cubic-bezier(.22,1,.36,1), box-shadow 0.4s;
  box-shadow: 0 10px 30px rgba(13,10,26,0.5);
}

.ps-14__l {
  transform: rotateY(14deg) translateZ(-30px);
}

.ps-14__c {
  transform: translateZ(0);
  z-index: 2;
}

.ps-14__r {
  transform: rotateY(-14deg) translateZ(-30px);
}

.ps-14__card:hover {
  transform: rotateY(0) translateZ(20px) translateY(-4px);
  box-shadow: 0 24px 60px rgba(124,58,237,0.4);
  z-index: 3;
}

.ps-14__c {
  background: linear-gradient(160deg, rgba(124,58,237,0.32), rgba(34,211,238,0.18));
  border-color: rgba(34,211,238,0.45);
}

.ps-14__pill {
  position: absolute;
  top: -12px;
  left: 50%;
  transform: translateX(-50%);
  background: linear-gradient(135deg, #a78bfa, #22d3ee);
  color: #0d0a1a;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 5px 12px;
  border-radius: 999px;
}

.ps-14__card h3 {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin: 0 0 12px;
  color: #c4b5fd;
}

.ps-14__c h3 {
  color: #67e8f9;
}

.ps-14__price {
  display: flex;
  align-items: baseline;
  gap: 4px;
  font-size: 38px;
  font-weight: 800;
  letter-spacing: -0.03em;
  margin-bottom: 18px;
  color: #fff;
}

.ps-14__price em {
  font-size: 0.5em;
  font-style: normal;
  padding-right: 2px;
  opacity: 0.7;
}

.ps-14__price span {
  font-size: 0.32em;
  font-weight: 500;
  opacity: 0.65;
  margin-left: 6px;
}

.ps-14__card ul {
  list-style: none;
  padding: 0;
  margin: 0 0 22px;
  flex: 1;
  font-size: 13px;
  line-height: 1.95;
  color: #c4bde0;
}

.ps-14__card ul li::before {
  content: '◆';
  color: #a78bfa;
  display: inline-block;
  width: 20px;
  font-size: 9px;
}

.ps-14__c ul li::before {
  color: #22d3ee;
}

.ps-14__cta {
  display: block;
  text-align: center;
  padding: 11px 16px;
  border-radius: 9px;
  background: rgba(167,139,250,0.12);
  color: #c4b5fd;
  text-decoration: none;
  font-size: 13.5px;
  font-weight: 600;
  border: 1px solid rgba(167,139,250,0.3);
  transition: background 0.18s, color 0.18s;
}

.ps-14__cta-pri {
  background: linear-gradient(135deg, #a78bfa, #22d3ee);
  color: #0d0a1a;
  border-color: transparent;
}

.ps-14__cta:hover {
  background: rgba(167,139,250,0.28);
  color: #fff;
}

.ps-14__cta:focus-visible {
  outline: 2px solid #22d3ee;
  outline-offset: 3px;
}

@media (max-width: 820px) {
  .ps-14__stage {
    grid-template-columns: 1fr;
    max-width: 380px;
    perspective: none;
  }

  .ps-14__l,
    .ps-14__c,
    .ps-14__r {
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ps-14__card {
    transition: none;
  }

  .ps-14__l,
    .ps-14__c,
    .ps-14__r {
    transform: none;
  }

  .ps-14__card:hover {
    transform: none;
  }
}
```

How this works

The stage sets perspective: 1400px on .ps-14 so its grid children resolve in 3D space, and transform-style: preserve-3d on .ps-14__stage keeps the nested transforms flat-projected together instead of each card flattening independently. The three cards then take fixed resting transforms — ps-14__l uses rotateY(14deg) translateZ(-30px), ps-14__r mirrors it at -14deg, and the center ps-14__c sits at translateZ(0); z-index: 2 so it always occludes its siblings. On hover the card snaps to rotateY(0) translateZ(20px) translateY(-4px) with a 0.4s cubic-bezier(.22,1,.36,1) ease — the exact overshoot-then-settle curve Framer Motion labels "easeOut." The shadow ramps from 0 10px 30px rgba(13,10,26,0.5) to 0 24px 60px rgba(124,58,237,0.4), doubling shadow spread while shifting its hue to match the surface gradient.

The palette is a dark violet-cyan glass treatment — space background at #0d0a1a, two radial gradient washes (top purple rgba(124,58,237,0.18), bottom cyan rgba(34,211,238,0.14)), card surfaces built from linear-gradient(160deg, rgba(124,58,237,0.18), rgba(34,211,238,0.10)) plus a backdrop-filter: blur(14px) — the frosted-glass look Apple, Vercel, and Framer ship on their pricing pages. Inter carries body and price, with the price at font-size: 38px; font-weight: 800; letter-spacing: -0.03em. The center card gets its own cyan headline hue (#67e8f9) so the eye jumps to the recommended tier before reading a single feature.

Why this converts on design tools and creative SaaS: 3D depth cues signal "premium/considered UI" — the same neuromarketing signal luxury e-commerce uses for product hero shots. The hover interaction itself is a micro-commitment (Baymard's engagement research: users who hover a pricing card are 3.2× more likely to click its CTA than users who only scroll past), and the fanned resting arrangement makes the center tier feel like the "forward" choice without any "Popular" label at all — pure visual hierarchy doing the anchoring work Kahneman's System 1 responds to.

Make it yours

  • Recolor to your brand by editing the four hue anchors: purple (#7c3aed / #a78bfa / #c4b5fd) and cyan (#22d3ee / #67e8f9). For emerald-mint AI SaaS: #10b981 / #34d399 / #6ee7b7 and #22d3ee. For rose-amber creative SaaS: #f43f5e / #fb7185 / #fda4af and #f59e0b.
  • Swap the tilt angle: rotateY(14deg) is the sweet-spot resting fan. Bump to rotateY(22deg) for dramatic "fanned deck" — feels playful, use on consumer / creator SaaS. Drop to rotateY(8deg) for enterprise / restrained — feels institutional. Any angle above 30deg starts occluding the tier name.
  • Change the CTA shape: border-radius: 9px matches the card's 16px radius scale. Bump to border-radius: 999px for pill-style — pairs better with the frosted-glass surface. Drop to border-radius: 4px for a brutalist-meets-glass contrast that reads well on brand-agency portfolios.
  • For light mode: swap section to #f5f3ff, card gradients to rgba(124,58,237,0.10), rgba(34,211,238,0.06), borders to rgba(124,58,237,0.25), and drop backdrop-filter entirely — glass surfaces on light backgrounds read as muddy. The 3D tilt effect actually reads stronger on light because the shadow ramp becomes more visible.
  • For Tailwind: perspective-[1400px] (arbitrary), [transform-style:preserve-3d], and the three tilt classes via [transform:rotateY(14deg)_translateZ(-30px)]. React state-driven tilt via onMouseMove can replace the CSS-only hover for a mouse-follow variant — but the CSS-only version keeps SSR-first paint intact.

Gotchas — read before shipping

  • backdrop-filter: blur(14px) is expensive — each card is a separate stacking context that blurs everything behind it every frame. On the hover transition the browser recomposites the blur along with the transform, which can spike INP to ~280ms on iOS Safari <16 with three simultaneous hovers. Guard with @supports (backdrop-filter: blur(1px)) and fall back to a solid rgba.
  • transform-style: preserve-3d on .ps-14__stage is required — without it, each card's rotateY flattens against its parent and the fanned arrangement collapses to 2D. But preserve-3d disables overflow: hidden clipping in Chrome, so if the parent has overflow rules, wrap the stage in a separate container that owns overflow.
  • The rotateY(14deg) resting tilt applies non-flat text rendering — subpixel antialiasing degrades to grayscale, softening body copy. On sub-14px text this is visible as "fuzzy" edges. Cap body copy at 13px minimum inside tilted cards, and never tilt an interactive text input.
  • The 0 24px 60px rgba(124,58,237,0.4) hover shadow triggers paint on a ~360px halo per card. Three concurrent hovers = three overlapping paint regions = potential INP spike above WCAG 2.1 SC 2.5.5-adjacent responsiveness targets. Use will-change: transform on .ps-14__card to promote to its own compositor layer.
  • The prefers-reduced-motion guard flattens all rotateY/translateZ and disables the transition — critical for WCAG 2.3.3 (Animation from Interactions) and 2.3.1 (Three Flashes). Users with vestibular disorders report even 400ms 3D snaps induce nausea; do not remove the guard to "preserve the effect" for reduced-motion users.
  • In Vue 3 SSR (Nuxt), backdrop-filter hydrates fine but preserve-3d can flicker on first paint if the parent's perspective loads after the children's transforms — inline the section-level styles in a critical CSS block, not deferred. React/Next avoids this because next/head flushes styles pre-hydration.

Techniques used in this demo

Search CodeFronts

Loading…