14 / 21
All 21 in 21 Tailwind CSS Pricing Sections
- 01Tailwind Classic Three Tier Pricing Section
- 02Tailwind Highlighted Popular Pricing Tier
- 03Tailwind Feature Comparison Pricing Table
- 04Tailwind Monthly Yearly Billing Toggle Pricing
- 05Tailwind Usage Slider Pricing Estimator
- 06Tailwind Single Premium Pricing Card
- 07Tailwind Bento Pricing Grid
- 08Tailwind Vertical Tier Timeline Pricing
- 09Tailwind Brutalist Print Pricing Zine
- 10Tailwind Glass Aurora Pricing Cards
- 11Tailwind Per-Seat Pricing Calculator
- 12Tailwind Animated Conic Border Pricing Cards
- 13Tailwind Editorial Two Column Pricing List
- 14Tailwind 3D Tilt Perspective Pricing Cards
- 15Tailwind Synthwave Neon Pricing Section
- 16Tailwind Pay What You Want Pricing Card
- 17Tailwind Currency Switcher Pricing Section
- 18Tailwind Social Proof Marquee Pricing
- 19Tailwind Feature Accordion Pricing Tiers
- 20Tailwind Cursor Spotlight Pricing Cards
- 21Tailwind Annual Savings Stripe Pricing
Tailwind 3D Tilt Perspective Pricing Cards
Three cards standing in a 1400px perspective — the outer two angled back, the centre one square on, any card snapping forward on hover. The port is where v4's per-axis transform utilities earn their keep: rotate-y-*, translate-z-* and translate-y-* compose through separate variables, so the hover state overrides one axis without restating the other two.
Complete Tailwind CSS landing page templates in HTML, React, Next.js and Vue · from $49
Cruip · Affiliate link
The code
<section class="tps-14 w-full min-h-svh perspective-[1400px] bg-tps14-bg bg-[radial-gradient(60%_60%_at_50%_0%,--alpha(--color-tps14-violet/18),transparent_60%),radial-gradient(60%_60%_at_50%_100%,--alpha(--color-tps14-cyan/14),transparent_60%)] font-tps14-sans text-tps14-body px-[clamp(20px,4vw,40px)] py-[clamp(28px,5vw,56px)]" aria-label="Pricing">
<header class="text-center mb-[clamp(24px,4vw,40px)]">
<h2 class="m-0 mb-2 text-[clamp(24px,3.6vw,36px)] font-bold tracking-[-0.02em] text-white">Pricing with depth.</h2>
<p class="m-0 text-sm text-tps14-muted">Hover to bring a plan forward.</p>
</header>
<div class="grid grid-cols-1 md:grid-cols-[repeat(3,minmax(0,1fr))] items-center gap-5 max-w-[380px] md:max-w-[980px] mx-auto transform-3d">
<article class="relative flex flex-col px-[22px] pt-[26px] pb-[22px] rounded-[16px] bg-linear-160 from-tps14-violet/18 to-tps14-cyan/10 border border-tps14-lilac/25 backdrop-blur-[14px] shadow-[0_10px_30px_--alpha(--color-tps14-bg/50)] transition-[transform,box-shadow] duration-[400ms] ease-tps14-out md:rotate-y-[14deg] md:translate-z-[-30px] hover:rotate-y-0 hover:translate-z-[20px] hover:-translate-y-1 hover:z-3 hover:shadow-[0_24px_60px_--alpha(--color-tps14-violet/40)] motion-reduce:transition-none motion-reduce:rotate-y-0 motion-reduce:translate-z-0 motion-reduce:hover:translate-y-0">
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps14-lilac">Lite</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>9<span class="ml-1.5 text-[0.32em] font-medium opacity-65">/mo</span></div>
<ul class="list-none p-0 m-0 mb-[22px] flex-1 text-[13px]/[1.95] text-tps14-body">
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">Personal projects</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">5 GB storage</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">Community support</li>
</ul>
<a href="#lite" class="block text-center px-4 py-2.75 rounded-[9px] bg-tps14-lilac/12 text-tps14-lilac no-underline text-[13.5px] font-semibold border border-tps14-lilac/30 transition-[background-color,color] duration-[180ms] hover:bg-tps14-lilac/28 hover:text-white focus-visible:outline-2 focus-visible:outline-tps14-cyan focus-visible:outline-offset-[3px] motion-reduce:transition-none">Pick Lite</a>
</article>
<article class="relative flex flex-col px-[22px] pt-[26px] pb-[22px] rounded-[16px] bg-linear-160 from-tps14-violet/32 to-tps14-cyan/18 border border-tps14-cyan/45 backdrop-blur-[14px] shadow-[0_10px_30px_--alpha(--color-tps14-bg/50)] transition-[transform,box-shadow] duration-[400ms] ease-tps14-out md:z-2 hover:translate-z-[20px] hover:-translate-y-1 hover:z-3 hover:shadow-[0_24px_60px_--alpha(--color-tps14-violet/40)] motion-reduce:transition-none motion-reduce:translate-z-0 motion-reduce:hover:translate-y-0">
<span class="absolute -top-3 left-1/2 -translate-x-1/2 px-3 py-1.25 rounded-full bg-linear-135 from-tps14-lilac to-tps14-cyan text-tps14-bg text-[10.5px] font-bold uppercase tracking-[0.08em]">Best fit</span>
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps14-cyan">Plus</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>29<span class="ml-1.5 text-[0.32em] font-medium opacity-65">/mo</span></div>
<ul class="list-none p-0 m-0 mb-[22px] flex-1 text-[13px]/[1.95] text-tps14-body">
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-cyan">Unlimited projects</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-cyan">50 GB storage</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-cyan">Priority support</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-cyan">Custom domains</li>
</ul>
<a href="#plus" class="block text-center px-4 py-2.75 rounded-[9px] bg-linear-135 from-tps14-lilac to-tps14-cyan text-tps14-bg no-underline text-[13.5px] font-semibold border border-transparent transition-[filter] duration-[180ms] hover:brightness-110 focus-visible:outline-2 focus-visible:outline-tps14-cyan focus-visible:outline-offset-[3px] motion-reduce:transition-none">Pick Plus</a>
</article>
<article class="relative flex flex-col px-[22px] pt-[26px] pb-[22px] rounded-[16px] bg-linear-160 from-tps14-violet/18 to-tps14-cyan/10 border border-tps14-lilac/25 backdrop-blur-[14px] shadow-[0_10px_30px_--alpha(--color-tps14-bg/50)] transition-[transform,box-shadow] duration-[400ms] ease-tps14-out md:rotate-y-[-14deg] md:translate-z-[-30px] hover:rotate-y-0 hover:translate-z-[20px] hover:-translate-y-1 hover:z-3 hover:shadow-[0_24px_60px_--alpha(--color-tps14-violet/40)] motion-reduce:transition-none motion-reduce:rotate-y-0 motion-reduce:translate-z-0 motion-reduce:hover:translate-y-0">
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps14-lilac">Power</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>79<span class="ml-1.5 text-[0.32em] font-medium opacity-65">/mo</span></div>
<ul class="list-none p-0 m-0 mb-[22px] flex-1 text-[13px]/[1.95] text-tps14-body">
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">Everything in Plus</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">SSO + audit logs</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">Dedicated CSM</li>
</ul>
<a href="#power" class="block text-center px-4 py-2.75 rounded-[9px] bg-tps14-lilac/12 text-tps14-lilac no-underline text-[13.5px] font-semibold border border-tps14-lilac/30 transition-[background-color,color] duration-[180ms] hover:bg-tps14-lilac/28 hover:text-white focus-visible:outline-2 focus-visible:outline-tps14-cyan focus-visible:outline-offset-[3px] motion-reduce:transition-none">Pick Power</a>
</article>
</div>
</section><section class="tps-14 w-full min-h-svh perspective-[1400px] bg-tps14-bg bg-[radial-gradient(60%_60%_at_50%_0%,--alpha(--color-tps14-violet/18),transparent_60%),radial-gradient(60%_60%_at_50%_100%,--alpha(--color-tps14-cyan/14),transparent_60%)] font-tps14-sans text-tps14-body px-[clamp(20px,4vw,40px)] py-[clamp(28px,5vw,56px)]" aria-label="Pricing">
<header class="text-center mb-[clamp(24px,4vw,40px)]">
<h2 class="m-0 mb-2 text-[clamp(24px,3.6vw,36px)] font-bold tracking-[-0.02em] text-white">Pricing with depth.</h2>
<p class="m-0 text-sm text-tps14-muted">Hover to bring a plan forward.</p>
</header>
<div class="grid grid-cols-1 md:grid-cols-[repeat(3,minmax(0,1fr))] items-center gap-5 max-w-[380px] md:max-w-[980px] mx-auto transform-3d">
<article class="relative flex flex-col px-[22px] pt-[26px] pb-[22px] rounded-[16px] bg-linear-160 from-tps14-violet/18 to-tps14-cyan/10 border border-tps14-lilac/25 backdrop-blur-[14px] shadow-[0_10px_30px_--alpha(--color-tps14-bg/50)] transition-[transform,box-shadow] duration-[400ms] ease-tps14-out md:rotate-y-[14deg] md:translate-z-[-30px] hover:rotate-y-0 hover:translate-z-[20px] hover:-translate-y-1 hover:z-3 hover:shadow-[0_24px_60px_--alpha(--color-tps14-violet/40)] motion-reduce:transition-none motion-reduce:rotate-y-0 motion-reduce:translate-z-0 motion-reduce:hover:translate-y-0">
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps14-lilac">Lite</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>9<span class="ml-1.5 text-[0.32em] font-medium opacity-65">/mo</span></div>
<ul class="list-none p-0 m-0 mb-[22px] flex-1 text-[13px]/[1.95] text-tps14-body">
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">Personal projects</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">5 GB storage</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">Community support</li>
</ul>
<a href="#lite" class="block text-center px-4 py-2.75 rounded-[9px] bg-tps14-lilac/12 text-tps14-lilac no-underline text-[13.5px] font-semibold border border-tps14-lilac/30 transition-[background-color,color] duration-[180ms] hover:bg-tps14-lilac/28 hover:text-white focus-visible:outline-2 focus-visible:outline-tps14-cyan focus-visible:outline-offset-[3px] motion-reduce:transition-none">Pick Lite</a>
</article>
<article class="relative flex flex-col px-[22px] pt-[26px] pb-[22px] rounded-[16px] bg-linear-160 from-tps14-violet/32 to-tps14-cyan/18 border border-tps14-cyan/45 backdrop-blur-[14px] shadow-[0_10px_30px_--alpha(--color-tps14-bg/50)] transition-[transform,box-shadow] duration-[400ms] ease-tps14-out md:z-2 hover:translate-z-[20px] hover:-translate-y-1 hover:z-3 hover:shadow-[0_24px_60px_--alpha(--color-tps14-violet/40)] motion-reduce:transition-none motion-reduce:translate-z-0 motion-reduce:hover:translate-y-0">
<span class="absolute -top-3 left-1/2 -translate-x-1/2 px-3 py-1.25 rounded-full bg-linear-135 from-tps14-lilac to-tps14-cyan text-tps14-bg text-[10.5px] font-bold uppercase tracking-[0.08em]">Best fit</span>
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps14-cyan">Plus</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>29<span class="ml-1.5 text-[0.32em] font-medium opacity-65">/mo</span></div>
<ul class="list-none p-0 m-0 mb-[22px] flex-1 text-[13px]/[1.95] text-tps14-body">
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-cyan">Unlimited projects</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-cyan">50 GB storage</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-cyan">Priority support</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-cyan">Custom domains</li>
</ul>
<a href="#plus" class="block text-center px-4 py-2.75 rounded-[9px] bg-linear-135 from-tps14-lilac to-tps14-cyan text-tps14-bg no-underline text-[13.5px] font-semibold border border-transparent transition-[filter] duration-[180ms] hover:brightness-110 focus-visible:outline-2 focus-visible:outline-tps14-cyan focus-visible:outline-offset-[3px] motion-reduce:transition-none">Pick Plus</a>
</article>
<article class="relative flex flex-col px-[22px] pt-[26px] pb-[22px] rounded-[16px] bg-linear-160 from-tps14-violet/18 to-tps14-cyan/10 border border-tps14-lilac/25 backdrop-blur-[14px] shadow-[0_10px_30px_--alpha(--color-tps14-bg/50)] transition-[transform,box-shadow] duration-[400ms] ease-tps14-out md:rotate-y-[-14deg] md:translate-z-[-30px] hover:rotate-y-0 hover:translate-z-[20px] hover:-translate-y-1 hover:z-3 hover:shadow-[0_24px_60px_--alpha(--color-tps14-violet/40)] motion-reduce:transition-none motion-reduce:rotate-y-0 motion-reduce:translate-z-0 motion-reduce:hover:translate-y-0">
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps14-lilac">Power</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>79<span class="ml-1.5 text-[0.32em] font-medium opacity-65">/mo</span></div>
<ul class="list-none p-0 m-0 mb-[22px] flex-1 text-[13px]/[1.95] text-tps14-body">
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">Everything in Plus</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">SSO + audit logs</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">Dedicated CSM</li>
</ul>
<a href="#power" class="block text-center px-4 py-2.75 rounded-[9px] bg-tps14-lilac/12 text-tps14-lilac no-underline text-[13.5px] font-semibold border border-tps14-lilac/30 transition-[background-color,color] duration-[180ms] hover:bg-tps14-lilac/28 hover:text-white focus-visible:outline-2 focus-visible:outline-tps14-cyan focus-visible:outline-offset-[3px] motion-reduce:transition-none">Pick Power</a>
</article>
</div>
</section>@import "tailwindcss";
@theme {
--color-tps14-bg: #0d0a1a;
--color-tps14-bg: oklch(0.15 0.04 290);
--color-tps14-body: #c4bde0;
--color-tps14-body: oklch(0.81 0.04 290);
--color-tps14-muted: #a8a0c8;
--color-tps14-muted: oklch(0.72 0.05 292);
--color-tps14-lilac: #c4b5fd;
--color-tps14-lilac: oklch(0.81 0.1 294);
--color-tps14-violet: #7c3aed;
--color-tps14-violet: oklch(0.54 0.25 294);
--color-tps14-cyan: #67e8f9;
--color-tps14-cyan: oklch(0.87 0.11 200);
--font-tps14-sans: 'Outfit', system-ui, sans-serif;
--ease-tps14-out: cubic-bezier(0.22, 1, 0.36, 1);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the section is sized correctly in the moment before the
browser build has compiled its utilities. */
.tps-14 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tps14-bg: #0d0a1a;
--color-tps14-bg: oklch(0.15 0.04 290);
--color-tps14-body: #c4bde0;
--color-tps14-body: oklch(0.81 0.04 290);
--color-tps14-muted: #a8a0c8;
--color-tps14-muted: oklch(0.72 0.05 292);
--color-tps14-lilac: #c4b5fd;
--color-tps14-lilac: oklch(0.81 0.1 294);
--color-tps14-violet: #7c3aed;
--color-tps14-violet: oklch(0.54 0.25 294);
--color-tps14-cyan: #67e8f9;
--color-tps14-cyan: oklch(0.87 0.11 200);
--font-tps14-sans: 'Outfit', system-ui, sans-serif;
--ease-tps14-out: cubic-bezier(0.22, 1, 0.36, 1);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the section is sized correctly in the moment before the
browser build has compiled its utilities. */
.tps-14 {
width: 100%;
min-height: 100svh;
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-tps14-bg: #0d0a1a;
--color-tps14-bg: oklch(0.15 0.04 290);
--color-tps14-body: #c4bde0;
--color-tps14-body: oklch(0.81 0.04 290);
--color-tps14-muted: #a8a0c8;
--color-tps14-muted: oklch(0.72 0.05 292);
--color-tps14-lilac: #c4b5fd;
--color-tps14-lilac: oklch(0.81 0.1 294);
--color-tps14-violet: #7c3aed;
--color-tps14-violet: oklch(0.54 0.25 294);
--color-tps14-cyan: #67e8f9;
--color-tps14-cyan: oklch(0.87 0.11 200);
--font-tps14-sans: 'Outfit', system-ui, sans-serif;
--ease-tps14-out: cubic-bezier(0.22, 1, 0.36, 1);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the section is sized correctly in the moment before the
browser build has compiled its utilities. */
.tps-14 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tps14-bg: #0d0a1a;
--color-tps14-bg: oklch(0.15 0.04 290);
--color-tps14-body: #c4bde0;
--color-tps14-body: oklch(0.81 0.04 290);
--color-tps14-muted: #a8a0c8;
--color-tps14-muted: oklch(0.72 0.05 292);
--color-tps14-lilac: #c4b5fd;
--color-tps14-lilac: oklch(0.81 0.1 294);
--color-tps14-violet: #7c3aed;
--color-tps14-violet: oklch(0.54 0.25 294);
--color-tps14-cyan: #67e8f9;
--color-tps14-cyan: oklch(0.87 0.11 200);
--font-tps14-sans: 'Outfit', system-ui, sans-serif;
--ease-tps14-out: cubic-bezier(0.22, 1, 0.36, 1);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the section is sized correctly in the moment before the
browser build has compiled its utilities. */
.tps-14 {
width: 100%;
min-height: 100svh;
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The port's real gain:
rotate-y-*,translate-z-*andtranslate-y-*each compile to their own variable, sohover:rotate-y-0 hover:translate-z-[20px] hover:-translate-y-1composes. The CSS:hoverrule has to write one combinedtransformand therefore restate every axis, including the ones it is not changing. perspective-[1400px]andtransform-3dare first-class utilities in v4 — no arbitrary properties needed for either, which is new.- The 820px media query that unsets
perspectiveand threetransform: nonerules collapses intomd:prefixes: flat is the default, 3D is the enhancement. --ease-tps14-outas a theme token mintsease-tps14-out, so the expo curve is named on the element rather than repeated as a literal in each transition..ps-14__c h3and.ps-14__c ul li::before— two descendant overrides for the featured card's cyan accents — becometext-tps14-cyanandbefore:text-tps14-cyanon the elements themselves.--alpha(--color-tps14-violet/40)inside the hover shadow keeps the glow tied to the same token as the card's gradient, instead of the CSS version's separatergba()literal.
Here's a working CSS demo 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: Tailwind 3D Tilt Perspective Pricing Cards
Source: https://codefronts.com/layouts/tailwind-css-pricing-sections/tailwind-3d-tilt-perspective-pricing-cards/
Three cards standing in a 1400px perspective — the outer two angled back, the centre one square on, any card snapping forward on hover. The port is where v4's per-axis transform utilities earn their keep: rotate-y-*, translate-z-* and translate-y-* compose through separate variables, so the hover state overrides one axis without restating the other two.
## HTML
```html
<section class="tps-14 w-full min-h-svh perspective-[1400px] bg-tps14-bg bg-[radial-gradient(60%_60%_at_50%_0%,--alpha(--color-tps14-violet/18),transparent_60%),radial-gradient(60%_60%_at_50%_100%,--alpha(--color-tps14-cyan/14),transparent_60%)] font-tps14-sans text-tps14-body px-[clamp(20px,4vw,40px)] py-[clamp(28px,5vw,56px)]" aria-label="Pricing">
<header class="text-center mb-[clamp(24px,4vw,40px)]">
<h2 class="m-0 mb-2 text-[clamp(24px,3.6vw,36px)] font-bold tracking-[-0.02em] text-white">Pricing with depth.</h2>
<p class="m-0 text-sm text-tps14-muted">Hover to bring a plan forward.</p>
</header>
<div class="grid grid-cols-1 md:grid-cols-[repeat(3,minmax(0,1fr))] items-center gap-5 max-w-[380px] md:max-w-[980px] mx-auto transform-3d">
<article class="relative flex flex-col px-[22px] pt-[26px] pb-[22px] rounded-[16px] bg-linear-160 from-tps14-violet/18 to-tps14-cyan/10 border border-tps14-lilac/25 backdrop-blur-[14px] shadow-[0_10px_30px_--alpha(--color-tps14-bg/50)] transition-[transform,box-shadow] duration-[400ms] ease-tps14-out md:rotate-y-[14deg] md:translate-z-[-30px] hover:rotate-y-0 hover:translate-z-[20px] hover:-translate-y-1 hover:z-3 hover:shadow-[0_24px_60px_--alpha(--color-tps14-violet/40)] motion-reduce:transition-none motion-reduce:rotate-y-0 motion-reduce:translate-z-0 motion-reduce:hover:translate-y-0">
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps14-lilac">Lite</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>9<span class="ml-1.5 text-[0.32em] font-medium opacity-65">/mo</span></div>
<ul class="list-none p-0 m-0 mb-[22px] flex-1 text-[13px]/[1.95] text-tps14-body">
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">Personal projects</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">5 GB storage</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">Community support</li>
</ul>
<a href="#lite" class="block text-center px-4 py-2.75 rounded-[9px] bg-tps14-lilac/12 text-tps14-lilac no-underline text-[13.5px] font-semibold border border-tps14-lilac/30 transition-[background-color,color] duration-[180ms] hover:bg-tps14-lilac/28 hover:text-white focus-visible:outline-2 focus-visible:outline-tps14-cyan focus-visible:outline-offset-[3px] motion-reduce:transition-none">Pick Lite</a>
</article>
<article class="relative flex flex-col px-[22px] pt-[26px] pb-[22px] rounded-[16px] bg-linear-160 from-tps14-violet/32 to-tps14-cyan/18 border border-tps14-cyan/45 backdrop-blur-[14px] shadow-[0_10px_30px_--alpha(--color-tps14-bg/50)] transition-[transform,box-shadow] duration-[400ms] ease-tps14-out md:z-2 hover:translate-z-[20px] hover:-translate-y-1 hover:z-3 hover:shadow-[0_24px_60px_--alpha(--color-tps14-violet/40)] motion-reduce:transition-none motion-reduce:translate-z-0 motion-reduce:hover:translate-y-0">
<span class="absolute -top-3 left-1/2 -translate-x-1/2 px-3 py-1.25 rounded-full bg-linear-135 from-tps14-lilac to-tps14-cyan text-tps14-bg text-[10.5px] font-bold uppercase tracking-[0.08em]">Best fit</span>
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps14-cyan">Plus</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>29<span class="ml-1.5 text-[0.32em] font-medium opacity-65">/mo</span></div>
<ul class="list-none p-0 m-0 mb-[22px] flex-1 text-[13px]/[1.95] text-tps14-body">
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-cyan">Unlimited projects</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-cyan">50 GB storage</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-cyan">Priority support</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-cyan">Custom domains</li>
</ul>
<a href="#plus" class="block text-center px-4 py-2.75 rounded-[9px] bg-linear-135 from-tps14-lilac to-tps14-cyan text-tps14-bg no-underline text-[13.5px] font-semibold border border-transparent transition-[filter] duration-[180ms] hover:brightness-110 focus-visible:outline-2 focus-visible:outline-tps14-cyan focus-visible:outline-offset-[3px] motion-reduce:transition-none">Pick Plus</a>
</article>
<article class="relative flex flex-col px-[22px] pt-[26px] pb-[22px] rounded-[16px] bg-linear-160 from-tps14-violet/18 to-tps14-cyan/10 border border-tps14-lilac/25 backdrop-blur-[14px] shadow-[0_10px_30px_--alpha(--color-tps14-bg/50)] transition-[transform,box-shadow] duration-[400ms] ease-tps14-out md:rotate-y-[-14deg] md:translate-z-[-30px] hover:rotate-y-0 hover:translate-z-[20px] hover:-translate-y-1 hover:z-3 hover:shadow-[0_24px_60px_--alpha(--color-tps14-violet/40)] motion-reduce:transition-none motion-reduce:rotate-y-0 motion-reduce:translate-z-0 motion-reduce:hover:translate-y-0">
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps14-lilac">Power</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>79<span class="ml-1.5 text-[0.32em] font-medium opacity-65">/mo</span></div>
<ul class="list-none p-0 m-0 mb-[22px] flex-1 text-[13px]/[1.95] text-tps14-body">
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">Everything in Plus</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">SSO + audit logs</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">Dedicated CSM</li>
</ul>
<a href="#power" class="block text-center px-4 py-2.75 rounded-[9px] bg-tps14-lilac/12 text-tps14-lilac no-underline text-[13.5px] font-semibold border border-tps14-lilac/30 transition-[background-color,color] duration-[180ms] hover:bg-tps14-lilac/28 hover:text-white focus-visible:outline-2 focus-visible:outline-tps14-cyan focus-visible:outline-offset-[3px] motion-reduce:transition-none">Pick Power</a>
</article>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tps14-bg: #0d0a1a;
--color-tps14-bg: oklch(0.15 0.04 290);
--color-tps14-body: #c4bde0;
--color-tps14-body: oklch(0.81 0.04 290);
--color-tps14-muted: #a8a0c8;
--color-tps14-muted: oklch(0.72 0.05 292);
--color-tps14-lilac: #c4b5fd;
--color-tps14-lilac: oklch(0.81 0.1 294);
--color-tps14-violet: #7c3aed;
--color-tps14-violet: oklch(0.54 0.25 294);
--color-tps14-cyan: #67e8f9;
--color-tps14-cyan: oklch(0.87 0.11 200);
--font-tps14-sans: 'Outfit', system-ui, sans-serif;
--ease-tps14-out: cubic-bezier(0.22, 1, 0.36, 1);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the section is sized correctly in the moment before the
browser build has compiled its utilities. */
.tps-14 {
width: 100%;
min-height: 100svh;
}
```Here's a working CSS demo 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: Tailwind 3D Tilt Perspective Pricing Cards
Source: https://codefronts.com/layouts/tailwind-css-pricing-sections/tailwind-3d-tilt-perspective-pricing-cards/
Three cards standing in a 1400px perspective — the outer two angled back, the centre one square on, any card snapping forward on hover. The port is where v4's per-axis transform utilities earn their keep: rotate-y-*, translate-z-* and translate-y-* compose through separate variables, so the hover state overrides one axis without restating the other two.
## HTML
```html
<section class="tps-14 w-full min-h-svh perspective-[1400px] bg-tps14-bg bg-[radial-gradient(60%_60%_at_50%_0%,--alpha(--color-tps14-violet/18),transparent_60%),radial-gradient(60%_60%_at_50%_100%,--alpha(--color-tps14-cyan/14),transparent_60%)] font-tps14-sans text-tps14-body px-[clamp(20px,4vw,40px)] py-[clamp(28px,5vw,56px)]" aria-label="Pricing">
<header class="text-center mb-[clamp(24px,4vw,40px)]">
<h2 class="m-0 mb-2 text-[clamp(24px,3.6vw,36px)] font-bold tracking-[-0.02em] text-white">Pricing with depth.</h2>
<p class="m-0 text-sm text-tps14-muted">Hover to bring a plan forward.</p>
</header>
<div class="grid grid-cols-1 md:grid-cols-[repeat(3,minmax(0,1fr))] items-center gap-5 max-w-[380px] md:max-w-[980px] mx-auto transform-3d">
<article class="relative flex flex-col px-[22px] pt-[26px] pb-[22px] rounded-[16px] bg-linear-160 from-tps14-violet/18 to-tps14-cyan/10 border border-tps14-lilac/25 backdrop-blur-[14px] shadow-[0_10px_30px_--alpha(--color-tps14-bg/50)] transition-[transform,box-shadow] duration-[400ms] ease-tps14-out md:rotate-y-[14deg] md:translate-z-[-30px] hover:rotate-y-0 hover:translate-z-[20px] hover:-translate-y-1 hover:z-3 hover:shadow-[0_24px_60px_--alpha(--color-tps14-violet/40)] motion-reduce:transition-none motion-reduce:rotate-y-0 motion-reduce:translate-z-0 motion-reduce:hover:translate-y-0">
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps14-lilac">Lite</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>9<span class="ml-1.5 text-[0.32em] font-medium opacity-65">/mo</span></div>
<ul class="list-none p-0 m-0 mb-[22px] flex-1 text-[13px]/[1.95] text-tps14-body">
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">Personal projects</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">5 GB storage</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">Community support</li>
</ul>
<a href="#lite" class="block text-center px-4 py-2.75 rounded-[9px] bg-tps14-lilac/12 text-tps14-lilac no-underline text-[13.5px] font-semibold border border-tps14-lilac/30 transition-[background-color,color] duration-[180ms] hover:bg-tps14-lilac/28 hover:text-white focus-visible:outline-2 focus-visible:outline-tps14-cyan focus-visible:outline-offset-[3px] motion-reduce:transition-none">Pick Lite</a>
</article>
<article class="relative flex flex-col px-[22px] pt-[26px] pb-[22px] rounded-[16px] bg-linear-160 from-tps14-violet/32 to-tps14-cyan/18 border border-tps14-cyan/45 backdrop-blur-[14px] shadow-[0_10px_30px_--alpha(--color-tps14-bg/50)] transition-[transform,box-shadow] duration-[400ms] ease-tps14-out md:z-2 hover:translate-z-[20px] hover:-translate-y-1 hover:z-3 hover:shadow-[0_24px_60px_--alpha(--color-tps14-violet/40)] motion-reduce:transition-none motion-reduce:translate-z-0 motion-reduce:hover:translate-y-0">
<span class="absolute -top-3 left-1/2 -translate-x-1/2 px-3 py-1.25 rounded-full bg-linear-135 from-tps14-lilac to-tps14-cyan text-tps14-bg text-[10.5px] font-bold uppercase tracking-[0.08em]">Best fit</span>
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps14-cyan">Plus</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>29<span class="ml-1.5 text-[0.32em] font-medium opacity-65">/mo</span></div>
<ul class="list-none p-0 m-0 mb-[22px] flex-1 text-[13px]/[1.95] text-tps14-body">
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-cyan">Unlimited projects</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-cyan">50 GB storage</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-cyan">Priority support</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-cyan">Custom domains</li>
</ul>
<a href="#plus" class="block text-center px-4 py-2.75 rounded-[9px] bg-linear-135 from-tps14-lilac to-tps14-cyan text-tps14-bg no-underline text-[13.5px] font-semibold border border-transparent transition-[filter] duration-[180ms] hover:brightness-110 focus-visible:outline-2 focus-visible:outline-tps14-cyan focus-visible:outline-offset-[3px] motion-reduce:transition-none">Pick Plus</a>
</article>
<article class="relative flex flex-col px-[22px] pt-[26px] pb-[22px] rounded-[16px] bg-linear-160 from-tps14-violet/18 to-tps14-cyan/10 border border-tps14-lilac/25 backdrop-blur-[14px] shadow-[0_10px_30px_--alpha(--color-tps14-bg/50)] transition-[transform,box-shadow] duration-[400ms] ease-tps14-out md:rotate-y-[-14deg] md:translate-z-[-30px] hover:rotate-y-0 hover:translate-z-[20px] hover:-translate-y-1 hover:z-3 hover:shadow-[0_24px_60px_--alpha(--color-tps14-violet/40)] motion-reduce:transition-none motion-reduce:rotate-y-0 motion-reduce:translate-z-0 motion-reduce:hover:translate-y-0">
<h3 class="m-0 mb-3 text-sm font-bold uppercase tracking-[0.1em] text-tps14-lilac">Power</h3>
<div class="flex items-baseline gap-1 mb-[18px] text-[38px] font-extrabold tracking-[-0.03em] text-white"><em class="not-italic text-[0.5em] pr-0.5 opacity-70">$</em>79<span class="ml-1.5 text-[0.32em] font-medium opacity-65">/mo</span></div>
<ul class="list-none p-0 m-0 mb-[22px] flex-1 text-[13px]/[1.95] text-tps14-body">
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">Everything in Plus</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">SSO + audit logs</li>
<li class="before:content-['\25C6'] before:inline-block before:w-5 before:text-[9px] before:text-tps14-lilac">Dedicated CSM</li>
</ul>
<a href="#power" class="block text-center px-4 py-2.75 rounded-[9px] bg-tps14-lilac/12 text-tps14-lilac no-underline text-[13.5px] font-semibold border border-tps14-lilac/30 transition-[background-color,color] duration-[180ms] hover:bg-tps14-lilac/28 hover:text-white focus-visible:outline-2 focus-visible:outline-tps14-cyan focus-visible:outline-offset-[3px] motion-reduce:transition-none">Pick Power</a>
</article>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tps14-bg: #0d0a1a;
--color-tps14-bg: oklch(0.15 0.04 290);
--color-tps14-body: #c4bde0;
--color-tps14-body: oklch(0.81 0.04 290);
--color-tps14-muted: #a8a0c8;
--color-tps14-muted: oklch(0.72 0.05 292);
--color-tps14-lilac: #c4b5fd;
--color-tps14-lilac: oklch(0.81 0.1 294);
--color-tps14-violet: #7c3aed;
--color-tps14-violet: oklch(0.54 0.25 294);
--color-tps14-cyan: #67e8f9;
--color-tps14-cyan: oklch(0.87 0.11 200);
--font-tps14-sans: 'Outfit', system-ui, sans-serif;
--ease-tps14-out: cubic-bezier(0.22, 1, 0.36, 1);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the section is sized correctly in the moment before the
browser build has compiled its utilities. */
.tps-14 {
width: 100%;
min-height: 100svh;
}
```How this works
Per-axis transforms are the whole reason this port is shorter than the CSS. v4 compiles each transform utility into its own custom property and composes them:
/* resting */ --tw-rotate-y: rotateY(14deg); --tw-translate-z: -30px;
/* hover */ --tw-rotate-y: rotateY(0deg); --tw-translate-z: 20px; --tw-translate-y: -4px;In the CSS version .ps-14__card:hover writes one composed transform that must repeat every axis it does not want to change. Here the hover rule touches only what moves.
Perspective belongs to the ancestor, depth to the child. perspective-[1400px] sits on the section so all three cards share one vanishing point; transform-3d on the grid keeps the children in the same 3D context rather than flattening them. Miss either and translate-z silently does nothing.
The tilt is a desktop enhancement. Everything 3D is behind md:, so the stacked mobile layout is flat by default — the CSS version needs a media query that unsets perspective and three transform: none rules.
Reduced motion removes the motion, not the design. motion-reduce: zeroes the tilt, the hover travel and the transition; the gradient surfaces, borders and shadows stay exactly as they are, so the section still reads as three distinct cards.
Make it yours
- Tilt angle: the
14degpair. Past about 20deg the outer cards' text starts to distort noticeably. - Depth:
md:translate-z-[-30px]resting andhover:translate-z-[20px]. The 50px swing is what makes the snap feel physical. - Perspective:
perspective-[1400px]. Smaller numbers exaggerate the effect — below 800px it becomes a funhouse. - Curve:
--ease-tps14-out. The overshoot-free expo curve is what stops the snap looking springy; swap it forease-outif you want it calmer. - Surface: the two
from-*/to-*stops on each card. The featured card is the same gradient at higher alpha, which is why the set reads as one material.
Gotchas — read before shipping
- Do not write the tilt as an arbitrary
[transform:…]— a composedtransformand v4's per-axis variables do not merge, and you would lose the free composition that makes the hover state one line. translate-z-*does nothing without a 3D context.transform-3don the grid andperspective-*on an ancestor are both required.- Keep the 3D behind
md:. Rotated cards in a single column read as a rendering bug. backdrop-blur-[14px]on a rotated element is expensive to composite. Three cards is fine; do not extend this pattern to a long list.- The pill is positioned with
-translate-x-1/2and the card isrelative— but the card must not beoverflow-hidden, or the pill is clipped at-top-3. - The featured card's cyan heading (
text-tps14-cyan) is on a translucent purple ground; it measures past 4.5:1, but dropping its opacity would not.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
3D transforms and transform-style: preserve-3d are ancient (Baseline since 2015). The interesting part is v4's per-axis rotate-y-* / translate-z-* utilities, which rely on custom-property composition and so need the same browsers as the rest of v4.