18 / 21
All 21 in 21 Tailwind CSS Pricing Tables
- 01Tailwind CSS Pricing Table with Monthly / Annual Toggle
- 02Tailwind Pricing Table with Currency Switcher (USD, GBP, EUR, AUD)
- 03Tailwind CSS Pricing Comparison Table with Expandable Features
- 04Tailwind Dark Mode Pricing Cards with Light / Dark Toggle
- 05Tailwind Pricing Cards with Hover Scale Effect
- 06Tailwind "Most Popular" Pricing Card with Animated Border Beam
- 07Tailwind Pricing Cards with Animated Discount Badges
- 08Tailwind Pricing Table CTA Button Hover Animation
- 09Tailwind Pricing Cards with Color-Changing Hover Themes
- 10Tailwind CSS 3D Flip Pricing Cards
- 11Tailwind Pricing Slider Calculator for Per-Seat Pricing
- 12Tailwind Tabbed Pricing Table for Multiple Products
- 13Tailwind Pricing Cards with Usage Meters & Progress Bars
- 14Tailwind Bundle Pricing Calculator with Checkboxes
- 15Glassmorphism Pricing Cards in Tailwind CSS
- 16Tailwind Pricing Card with Animated Gradient Border on Hover
- 17Tailwind Pricing Cards with Corner Ribbon & Bookmark Banner
- 18Neumorphic Pricing Table in Tailwind CSS (Soft UI)
- 19Mobile Swipe Pricing Carousel with Tailwind Scroll Snap
- 20Responsive Tailwind Pricing Table that Stacks into an Accordion
- 21Tailwind Pricing Cards with Skeleton Loading Shimmer
Neumorphic Pricing Table in Tailwind CSS (Soft UI)
Tactile soft-UI pricing: cards extrude from a same-colour surface with the signature light/dark shadow pair, and the selected plan's button sinks into the surface. v4's separate inset-shadow-* utility makes the press honest — has-checked:shadow-none has-checked:inset-shadow-[…] — and because neumorphism is weak on contrast, a real accent ring and a “Selected” label carry the state.
Complete Tailwind CSS landing page templates in HTML, React, Next.js and Vue · from $49
Cruip · Affiliate link
The code
<section class="tpt-18 w-full min-h-svh grid place-items-center bg-tpt18-base px-5 py-16 font-tpt18-sans text-tpt18-ink antialiased" aria-label="Neumorphic pricing tiers">
<div class="grid w-full max-w-5xl justify-items-center gap-11">
<h2 class="text-center text-[clamp(26px,3.8vw,40px)] font-extrabold tracking-[-0.02em]">Choose your calm</h2>
<fieldset class="grid w-full gap-7 md:grid-cols-3">
<legend class="sr-only">Plan</legend>
<label class="group/c flex cursor-pointer flex-col gap-2 rounded-[26px] border-2 border-transparent bg-tpt18-base p-8 shadow-[-9px_-9px_20px_var(--color-tpt18-lite),9px_9px_20px_var(--color-tpt18-shade)] transition-[box-shadow,border-color,translate] duration-300 hover:-translate-y-0.5 has-checked:border-tpt18-acc has-checked:shadow-[-4px_-4px_12px_var(--color-tpt18-lite),4px_4px_12px_var(--color-tpt18-shade)] has-focus-visible:outline-3 has-focus-visible:outline-offset-4 has-focus-visible:outline-tpt18-acc motion-reduce:hover:translate-y-0">
<input type="radio" name="tpt18-plan" value="calm" class="sr-only">
<h3 class="text-[13px] font-extrabold tracking-[0.1em] text-tpt18-mut uppercase">Calm</h3>
<p class="text-[44px] leading-tight font-extrabold tracking-[-0.03em]">$7<small class="text-[15px] font-bold tracking-normal text-tpt18-mut">/mo</small></p>
<ul class="mt-2 mb-6 grid flex-1 content-start gap-2.5 text-sm font-medium text-tpt18-body *:flex *:items-center *:gap-3 *:before:size-2 *:before:rounded-full *:before:bg-tpt18-acc *:before:content-['']"><li>10 sessions / month</li><li>Sleep stories</li></ul>
<span class="grid min-h-12 place-items-center rounded-2xl bg-tpt18-base text-sm font-extrabold text-tpt18-ink shadow-[-5px_-5px_12px_var(--color-tpt18-lite),5px_5px_12px_var(--color-tpt18-shade)] transition-[box-shadow,color,translate] duration-200 group-hover/c:text-tpt18-acc group-active/c:translate-y-px group-active/c:shadow-none group-active/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)] group-has-checked/c:text-tpt18-acc group-has-checked/c:shadow-none group-has-checked/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)]"><span class="group-has-checked/c:hidden">Select</span><span class="hidden group-has-checked/c:inline">✓ Selected</span></span>
</label>
<label class="group/c flex cursor-pointer flex-col gap-2 rounded-[26px] border-2 border-transparent bg-tpt18-base p-8 shadow-[-9px_-9px_20px_var(--color-tpt18-lite),9px_9px_20px_var(--color-tpt18-shade)] transition-[box-shadow,border-color,translate] duration-300 hover:-translate-y-0.5 has-checked:border-tpt18-acc has-checked:shadow-[-4px_-4px_12px_var(--color-tpt18-lite),4px_4px_12px_var(--color-tpt18-shade)] has-focus-visible:outline-3 has-focus-visible:outline-offset-4 has-focus-visible:outline-tpt18-acc motion-reduce:hover:translate-y-0">
<input type="radio" name="tpt18-plan" value="focus" class="sr-only" checked>
<h3 class="text-[13px] font-extrabold tracking-[0.1em] text-tpt18-mut uppercase">Focus</h3>
<p class="text-[44px] leading-tight font-extrabold tracking-[-0.03em]">$14<small class="text-[15px] font-bold tracking-normal text-tpt18-mut">/mo</small></p>
<ul class="mt-2 mb-6 grid flex-1 content-start gap-2.5 text-sm font-medium text-tpt18-body *:flex *:items-center *:gap-3 *:before:size-2 *:before:rounded-full *:before:bg-tpt18-acc *:before:content-['']"><li>Unlimited sessions</li><li>Focus soundscapes</li><li>Mood & streak stats</li></ul>
<span class="grid min-h-12 place-items-center rounded-2xl bg-tpt18-base text-sm font-extrabold text-tpt18-ink shadow-[-5px_-5px_12px_var(--color-tpt18-lite),5px_5px_12px_var(--color-tpt18-shade)] transition-[box-shadow,color,translate] duration-200 group-hover/c:text-tpt18-acc group-active/c:translate-y-px group-active/c:shadow-none group-active/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)] group-has-checked/c:text-tpt18-acc group-has-checked/c:shadow-none group-has-checked/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)]"><span class="group-has-checked/c:hidden">Select</span><span class="hidden group-has-checked/c:inline">✓ Selected</span></span>
</label>
<label class="group/c flex cursor-pointer flex-col gap-2 rounded-[26px] border-2 border-transparent bg-tpt18-base p-8 shadow-[-9px_-9px_20px_var(--color-tpt18-lite),9px_9px_20px_var(--color-tpt18-shade)] transition-[box-shadow,border-color,translate] duration-300 hover:-translate-y-0.5 has-checked:border-tpt18-acc has-checked:shadow-[-4px_-4px_12px_var(--color-tpt18-lite),4px_4px_12px_var(--color-tpt18-shade)] has-focus-visible:outline-3 has-focus-visible:outline-offset-4 has-focus-visible:outline-tpt18-acc motion-reduce:hover:translate-y-0">
<input type="radio" name="tpt18-plan" value="flow" class="sr-only">
<h3 class="text-[13px] font-extrabold tracking-[0.1em] text-tpt18-mut uppercase">Flow</h3>
<p class="text-[44px] leading-tight font-extrabold tracking-[-0.03em]">$21<small class="text-[15px] font-bold tracking-normal text-tpt18-mut">/mo</small></p>
<ul class="mt-2 mb-6 grid flex-1 content-start gap-2.5 text-sm font-medium text-tpt18-body *:flex *:items-center *:gap-3 *:before:size-2 *:before:rounded-full *:before:bg-tpt18-acc *:before:content-['']"><li>Everything in Focus</li><li>Weekly 1:1 coaching</li></ul>
<span class="grid min-h-12 place-items-center rounded-2xl bg-tpt18-base text-sm font-extrabold text-tpt18-ink shadow-[-5px_-5px_12px_var(--color-tpt18-lite),5px_5px_12px_var(--color-tpt18-shade)] transition-[box-shadow,color,translate] duration-200 group-hover/c:text-tpt18-acc group-active/c:translate-y-px group-active/c:shadow-none group-active/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)] group-has-checked/c:text-tpt18-acc group-has-checked/c:shadow-none group-has-checked/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)]"><span class="group-has-checked/c:hidden">Select</span><span class="hidden group-has-checked/c:inline">✓ Selected</span></span>
</label>
</fieldset>
<p aria-hidden="true" class="rounded-full px-4 py-2 font-tpt18-mono text-[11.5px] text-tpt18-mut shadow-[inset_-3px_-3px_8px_var(--color-tpt18-lite),inset_3px_3px_8px_var(--color-tpt18-shade)]">shadow-[light,dark] · has-checked:inset-shadow-[…] · radio cards</p>
</div>
</section><section class="tpt-18 w-full min-h-svh grid place-items-center bg-tpt18-base px-5 py-16 font-tpt18-sans text-tpt18-ink antialiased" aria-label="Neumorphic pricing tiers">
<div class="grid w-full max-w-5xl justify-items-center gap-11">
<h2 class="text-center text-[clamp(26px,3.8vw,40px)] font-extrabold tracking-[-0.02em]">Choose your calm</h2>
<fieldset class="grid w-full gap-7 md:grid-cols-3">
<legend class="sr-only">Plan</legend>
<label class="group/c flex cursor-pointer flex-col gap-2 rounded-[26px] border-2 border-transparent bg-tpt18-base p-8 shadow-[-9px_-9px_20px_var(--color-tpt18-lite),9px_9px_20px_var(--color-tpt18-shade)] transition-[box-shadow,border-color,translate] duration-300 hover:-translate-y-0.5 has-checked:border-tpt18-acc has-checked:shadow-[-4px_-4px_12px_var(--color-tpt18-lite),4px_4px_12px_var(--color-tpt18-shade)] has-focus-visible:outline-3 has-focus-visible:outline-offset-4 has-focus-visible:outline-tpt18-acc motion-reduce:hover:translate-y-0">
<input type="radio" name="tpt18-plan" value="calm" class="sr-only">
<h3 class="text-[13px] font-extrabold tracking-[0.1em] text-tpt18-mut uppercase">Calm</h3>
<p class="text-[44px] leading-tight font-extrabold tracking-[-0.03em]">$7<small class="text-[15px] font-bold tracking-normal text-tpt18-mut">/mo</small></p>
<ul class="mt-2 mb-6 grid flex-1 content-start gap-2.5 text-sm font-medium text-tpt18-body *:flex *:items-center *:gap-3 *:before:size-2 *:before:rounded-full *:before:bg-tpt18-acc *:before:content-['']"><li>10 sessions / month</li><li>Sleep stories</li></ul>
<span class="grid min-h-12 place-items-center rounded-2xl bg-tpt18-base text-sm font-extrabold text-tpt18-ink shadow-[-5px_-5px_12px_var(--color-tpt18-lite),5px_5px_12px_var(--color-tpt18-shade)] transition-[box-shadow,color,translate] duration-200 group-hover/c:text-tpt18-acc group-active/c:translate-y-px group-active/c:shadow-none group-active/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)] group-has-checked/c:text-tpt18-acc group-has-checked/c:shadow-none group-has-checked/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)]"><span class="group-has-checked/c:hidden">Select</span><span class="hidden group-has-checked/c:inline">✓ Selected</span></span>
</label>
<label class="group/c flex cursor-pointer flex-col gap-2 rounded-[26px] border-2 border-transparent bg-tpt18-base p-8 shadow-[-9px_-9px_20px_var(--color-tpt18-lite),9px_9px_20px_var(--color-tpt18-shade)] transition-[box-shadow,border-color,translate] duration-300 hover:-translate-y-0.5 has-checked:border-tpt18-acc has-checked:shadow-[-4px_-4px_12px_var(--color-tpt18-lite),4px_4px_12px_var(--color-tpt18-shade)] has-focus-visible:outline-3 has-focus-visible:outline-offset-4 has-focus-visible:outline-tpt18-acc motion-reduce:hover:translate-y-0">
<input type="radio" name="tpt18-plan" value="focus" class="sr-only" checked>
<h3 class="text-[13px] font-extrabold tracking-[0.1em] text-tpt18-mut uppercase">Focus</h3>
<p class="text-[44px] leading-tight font-extrabold tracking-[-0.03em]">$14<small class="text-[15px] font-bold tracking-normal text-tpt18-mut">/mo</small></p>
<ul class="mt-2 mb-6 grid flex-1 content-start gap-2.5 text-sm font-medium text-tpt18-body *:flex *:items-center *:gap-3 *:before:size-2 *:before:rounded-full *:before:bg-tpt18-acc *:before:content-['']"><li>Unlimited sessions</li><li>Focus soundscapes</li><li>Mood & streak stats</li></ul>
<span class="grid min-h-12 place-items-center rounded-2xl bg-tpt18-base text-sm font-extrabold text-tpt18-ink shadow-[-5px_-5px_12px_var(--color-tpt18-lite),5px_5px_12px_var(--color-tpt18-shade)] transition-[box-shadow,color,translate] duration-200 group-hover/c:text-tpt18-acc group-active/c:translate-y-px group-active/c:shadow-none group-active/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)] group-has-checked/c:text-tpt18-acc group-has-checked/c:shadow-none group-has-checked/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)]"><span class="group-has-checked/c:hidden">Select</span><span class="hidden group-has-checked/c:inline">✓ Selected</span></span>
</label>
<label class="group/c flex cursor-pointer flex-col gap-2 rounded-[26px] border-2 border-transparent bg-tpt18-base p-8 shadow-[-9px_-9px_20px_var(--color-tpt18-lite),9px_9px_20px_var(--color-tpt18-shade)] transition-[box-shadow,border-color,translate] duration-300 hover:-translate-y-0.5 has-checked:border-tpt18-acc has-checked:shadow-[-4px_-4px_12px_var(--color-tpt18-lite),4px_4px_12px_var(--color-tpt18-shade)] has-focus-visible:outline-3 has-focus-visible:outline-offset-4 has-focus-visible:outline-tpt18-acc motion-reduce:hover:translate-y-0">
<input type="radio" name="tpt18-plan" value="flow" class="sr-only">
<h3 class="text-[13px] font-extrabold tracking-[0.1em] text-tpt18-mut uppercase">Flow</h3>
<p class="text-[44px] leading-tight font-extrabold tracking-[-0.03em]">$21<small class="text-[15px] font-bold tracking-normal text-tpt18-mut">/mo</small></p>
<ul class="mt-2 mb-6 grid flex-1 content-start gap-2.5 text-sm font-medium text-tpt18-body *:flex *:items-center *:gap-3 *:before:size-2 *:before:rounded-full *:before:bg-tpt18-acc *:before:content-['']"><li>Everything in Focus</li><li>Weekly 1:1 coaching</li></ul>
<span class="grid min-h-12 place-items-center rounded-2xl bg-tpt18-base text-sm font-extrabold text-tpt18-ink shadow-[-5px_-5px_12px_var(--color-tpt18-lite),5px_5px_12px_var(--color-tpt18-shade)] transition-[box-shadow,color,translate] duration-200 group-hover/c:text-tpt18-acc group-active/c:translate-y-px group-active/c:shadow-none group-active/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)] group-has-checked/c:text-tpt18-acc group-has-checked/c:shadow-none group-has-checked/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)]"><span class="group-has-checked/c:hidden">Select</span><span class="hidden group-has-checked/c:inline">✓ Selected</span></span>
</label>
</fieldset>
<p aria-hidden="true" class="rounded-full px-4 py-2 font-tpt18-mono text-[11.5px] text-tpt18-mut shadow-[inset_-3px_-3px_8px_var(--color-tpt18-lite),inset_3px_3px_8px_var(--color-tpt18-shade)]">shadow-[light,dark] · has-checked:inset-shadow-[…] · radio cards</p>
</div>
</section>@import "tailwindcss";
@theme {
--color-tpt18-base: #e4e9f2;
--color-tpt18-base: oklch(0.93 0.012 260);
--color-tpt18-lite: #f8faff;
--color-tpt18-lite: oklch(0.985 0.005 260);
--color-tpt18-shade: #b9c1d3;
--color-tpt18-shade: oklch(0.8 0.025 262);
--color-tpt18-ink: #262c3c;
--color-tpt18-ink: oklch(0.28 0.03 265);
--color-tpt18-body: #404863;
--color-tpt18-body: oklch(0.4 0.04 265);
--color-tpt18-mut: #535c77;
--color-tpt18-mut: oklch(0.47 0.04 265);
--color-tpt18-acc: #4252c9;
--color-tpt18-acc: oklch(0.5 0.18 268);
--font-tpt18-sans: 'Nunito', system-ui, sans-serif;
--font-tpt18-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities. */
.tpt-18 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tpt18-base: #e4e9f2;
--color-tpt18-base: oklch(0.93 0.012 260);
--color-tpt18-lite: #f8faff;
--color-tpt18-lite: oklch(0.985 0.005 260);
--color-tpt18-shade: #b9c1d3;
--color-tpt18-shade: oklch(0.8 0.025 262);
--color-tpt18-ink: #262c3c;
--color-tpt18-ink: oklch(0.28 0.03 265);
--color-tpt18-body: #404863;
--color-tpt18-body: oklch(0.4 0.04 265);
--color-tpt18-mut: #535c77;
--color-tpt18-mut: oklch(0.47 0.04 265);
--color-tpt18-acc: #4252c9;
--color-tpt18-acc: oklch(0.5 0.18 268);
--font-tpt18-sans: 'Nunito', system-ui, sans-serif;
--font-tpt18-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities. */
.tpt-18 {
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-tpt18-base: #e4e9f2;
--color-tpt18-base: oklch(0.93 0.012 260);
--color-tpt18-lite: #f8faff;
--color-tpt18-lite: oklch(0.985 0.005 260);
--color-tpt18-shade: #b9c1d3;
--color-tpt18-shade: oklch(0.8 0.025 262);
--color-tpt18-ink: #262c3c;
--color-tpt18-ink: oklch(0.28 0.03 265);
--color-tpt18-body: #404863;
--color-tpt18-body: oklch(0.4 0.04 265);
--color-tpt18-mut: #535c77;
--color-tpt18-mut: oklch(0.47 0.04 265);
--color-tpt18-acc: #4252c9;
--color-tpt18-acc: oklch(0.5 0.18 268);
--font-tpt18-sans: 'Nunito', system-ui, sans-serif;
--font-tpt18-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities. */
.tpt-18 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tpt18-base: #e4e9f2;
--color-tpt18-base: oklch(0.93 0.012 260);
--color-tpt18-lite: #f8faff;
--color-tpt18-lite: oklch(0.985 0.005 260);
--color-tpt18-shade: #b9c1d3;
--color-tpt18-shade: oklch(0.8 0.025 262);
--color-tpt18-ink: #262c3c;
--color-tpt18-ink: oklch(0.28 0.03 265);
--color-tpt18-body: #404863;
--color-tpt18-body: oklch(0.4 0.04 265);
--color-tpt18-mut: #535c77;
--color-tpt18-mut: oklch(0.47 0.04 265);
--color-tpt18-acc: #4252c9;
--color-tpt18-acc: oklch(0.5 0.18 268);
--font-tpt18-sans: 'Nunito', system-ui, sans-serif;
--font-tpt18-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities. */
.tpt-18 {
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=Nunito:wght@400..900&family=JetBrains+Mono:wght@400;500&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=Nunito:wght@400..900&family=JetBrains+Mono:wght@400;500&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The
--lite/--shadecolor-mix()derivations became named@themetokens, used inside arbitraryshadow-[…]values. - The original's inset
box-shadowswap became v4'sshadow-none+inset-shadow-[…]pair, so raised and pressed are separate layers that crossfade. - UI upgrade from the CSS original: the selected card's button now reads “✓ Selected” (the original's selected and unselected buttons said the same thing), and the grid is a
<fieldset>with a legend instead of a barerole="radiogroup". - UI upgrade: Nunito's rounded terminals match the soft surfaces; muted copy rose from
#7d86a0(3.0:1 on the grey base) to 5.6:1, and bullets became drawn dots instead of an 8px “●” glyph.
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: Neumorphic Pricing Table in Tailwind CSS (Soft UI)
Source: https://codefronts.com/components/tailwind-css-pricing-tables/neumorphic-pricing-table-tailwind-css/
Tactile soft-UI pricing: cards extrude from a same-colour surface with the signature light/dark shadow pair, and the selected plan's button sinks into the surface. v4's separate inset-shadow-* utility makes the press honest — has-checked:shadow-none has-checked:inset-shadow-[…] — and because neumorphism is weak on contrast, a real accent ring and a “Selected” label carry the state.
## HTML
```html
<section class="tpt-18 w-full min-h-svh grid place-items-center bg-tpt18-base px-5 py-16 font-tpt18-sans text-tpt18-ink antialiased" aria-label="Neumorphic pricing tiers">
<div class="grid w-full max-w-5xl justify-items-center gap-11">
<h2 class="text-center text-[clamp(26px,3.8vw,40px)] font-extrabold tracking-[-0.02em]">Choose your calm</h2>
<fieldset class="grid w-full gap-7 md:grid-cols-3">
<legend class="sr-only">Plan</legend>
<label class="group/c flex cursor-pointer flex-col gap-2 rounded-[26px] border-2 border-transparent bg-tpt18-base p-8 shadow-[-9px_-9px_20px_var(--color-tpt18-lite),9px_9px_20px_var(--color-tpt18-shade)] transition-[box-shadow,border-color,translate] duration-300 hover:-translate-y-0.5 has-checked:border-tpt18-acc has-checked:shadow-[-4px_-4px_12px_var(--color-tpt18-lite),4px_4px_12px_var(--color-tpt18-shade)] has-focus-visible:outline-3 has-focus-visible:outline-offset-4 has-focus-visible:outline-tpt18-acc motion-reduce:hover:translate-y-0">
<input type="radio" name="tpt18-plan" value="calm" class="sr-only">
<h3 class="text-[13px] font-extrabold tracking-[0.1em] text-tpt18-mut uppercase">Calm</h3>
<p class="text-[44px] leading-tight font-extrabold tracking-[-0.03em]">$7<small class="text-[15px] font-bold tracking-normal text-tpt18-mut">/mo</small></p>
<ul class="mt-2 mb-6 grid flex-1 content-start gap-2.5 text-sm font-medium text-tpt18-body *:flex *:items-center *:gap-3 *:before:size-2 *:before:rounded-full *:before:bg-tpt18-acc *:before:content-['']"><li>10 sessions / month</li><li>Sleep stories</li></ul>
<span class="grid min-h-12 place-items-center rounded-2xl bg-tpt18-base text-sm font-extrabold text-tpt18-ink shadow-[-5px_-5px_12px_var(--color-tpt18-lite),5px_5px_12px_var(--color-tpt18-shade)] transition-[box-shadow,color,translate] duration-200 group-hover/c:text-tpt18-acc group-active/c:translate-y-px group-active/c:shadow-none group-active/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)] group-has-checked/c:text-tpt18-acc group-has-checked/c:shadow-none group-has-checked/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)]"><span class="group-has-checked/c:hidden">Select</span><span class="hidden group-has-checked/c:inline">✓ Selected</span></span>
</label>
<label class="group/c flex cursor-pointer flex-col gap-2 rounded-[26px] border-2 border-transparent bg-tpt18-base p-8 shadow-[-9px_-9px_20px_var(--color-tpt18-lite),9px_9px_20px_var(--color-tpt18-shade)] transition-[box-shadow,border-color,translate] duration-300 hover:-translate-y-0.5 has-checked:border-tpt18-acc has-checked:shadow-[-4px_-4px_12px_var(--color-tpt18-lite),4px_4px_12px_var(--color-tpt18-shade)] has-focus-visible:outline-3 has-focus-visible:outline-offset-4 has-focus-visible:outline-tpt18-acc motion-reduce:hover:translate-y-0">
<input type="radio" name="tpt18-plan" value="focus" class="sr-only" checked>
<h3 class="text-[13px] font-extrabold tracking-[0.1em] text-tpt18-mut uppercase">Focus</h3>
<p class="text-[44px] leading-tight font-extrabold tracking-[-0.03em]">$14<small class="text-[15px] font-bold tracking-normal text-tpt18-mut">/mo</small></p>
<ul class="mt-2 mb-6 grid flex-1 content-start gap-2.5 text-sm font-medium text-tpt18-body *:flex *:items-center *:gap-3 *:before:size-2 *:before:rounded-full *:before:bg-tpt18-acc *:before:content-['']"><li>Unlimited sessions</li><li>Focus soundscapes</li><li>Mood & streak stats</li></ul>
<span class="grid min-h-12 place-items-center rounded-2xl bg-tpt18-base text-sm font-extrabold text-tpt18-ink shadow-[-5px_-5px_12px_var(--color-tpt18-lite),5px_5px_12px_var(--color-tpt18-shade)] transition-[box-shadow,color,translate] duration-200 group-hover/c:text-tpt18-acc group-active/c:translate-y-px group-active/c:shadow-none group-active/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)] group-has-checked/c:text-tpt18-acc group-has-checked/c:shadow-none group-has-checked/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)]"><span class="group-has-checked/c:hidden">Select</span><span class="hidden group-has-checked/c:inline">✓ Selected</span></span>
</label>
<label class="group/c flex cursor-pointer flex-col gap-2 rounded-[26px] border-2 border-transparent bg-tpt18-base p-8 shadow-[-9px_-9px_20px_var(--color-tpt18-lite),9px_9px_20px_var(--color-tpt18-shade)] transition-[box-shadow,border-color,translate] duration-300 hover:-translate-y-0.5 has-checked:border-tpt18-acc has-checked:shadow-[-4px_-4px_12px_var(--color-tpt18-lite),4px_4px_12px_var(--color-tpt18-shade)] has-focus-visible:outline-3 has-focus-visible:outline-offset-4 has-focus-visible:outline-tpt18-acc motion-reduce:hover:translate-y-0">
<input type="radio" name="tpt18-plan" value="flow" class="sr-only">
<h3 class="text-[13px] font-extrabold tracking-[0.1em] text-tpt18-mut uppercase">Flow</h3>
<p class="text-[44px] leading-tight font-extrabold tracking-[-0.03em]">$21<small class="text-[15px] font-bold tracking-normal text-tpt18-mut">/mo</small></p>
<ul class="mt-2 mb-6 grid flex-1 content-start gap-2.5 text-sm font-medium text-tpt18-body *:flex *:items-center *:gap-3 *:before:size-2 *:before:rounded-full *:before:bg-tpt18-acc *:before:content-['']"><li>Everything in Focus</li><li>Weekly 1:1 coaching</li></ul>
<span class="grid min-h-12 place-items-center rounded-2xl bg-tpt18-base text-sm font-extrabold text-tpt18-ink shadow-[-5px_-5px_12px_var(--color-tpt18-lite),5px_5px_12px_var(--color-tpt18-shade)] transition-[box-shadow,color,translate] duration-200 group-hover/c:text-tpt18-acc group-active/c:translate-y-px group-active/c:shadow-none group-active/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)] group-has-checked/c:text-tpt18-acc group-has-checked/c:shadow-none group-has-checked/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)]"><span class="group-has-checked/c:hidden">Select</span><span class="hidden group-has-checked/c:inline">✓ Selected</span></span>
</label>
</fieldset>
<p aria-hidden="true" class="rounded-full px-4 py-2 font-tpt18-mono text-[11.5px] text-tpt18-mut shadow-[inset_-3px_-3px_8px_var(--color-tpt18-lite),inset_3px_3px_8px_var(--color-tpt18-shade)]">shadow-[light,dark] · has-checked:inset-shadow-[…] · radio cards</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tpt18-base: #e4e9f2;
--color-tpt18-base: oklch(0.93 0.012 260);
--color-tpt18-lite: #f8faff;
--color-tpt18-lite: oklch(0.985 0.005 260);
--color-tpt18-shade: #b9c1d3;
--color-tpt18-shade: oklch(0.8 0.025 262);
--color-tpt18-ink: #262c3c;
--color-tpt18-ink: oklch(0.28 0.03 265);
--color-tpt18-body: #404863;
--color-tpt18-body: oklch(0.4 0.04 265);
--color-tpt18-mut: #535c77;
--color-tpt18-mut: oklch(0.47 0.04 265);
--color-tpt18-acc: #4252c9;
--color-tpt18-acc: oklch(0.5 0.18 268);
--font-tpt18-sans: 'Nunito', system-ui, sans-serif;
--font-tpt18-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities. */
.tpt-18 {
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: Neumorphic Pricing Table in Tailwind CSS (Soft UI)
Source: https://codefronts.com/components/tailwind-css-pricing-tables/neumorphic-pricing-table-tailwind-css/
Tactile soft-UI pricing: cards extrude from a same-colour surface with the signature light/dark shadow pair, and the selected plan's button sinks into the surface. v4's separate inset-shadow-* utility makes the press honest — has-checked:shadow-none has-checked:inset-shadow-[…] — and because neumorphism is weak on contrast, a real accent ring and a “Selected” label carry the state.
## HTML
```html
<section class="tpt-18 w-full min-h-svh grid place-items-center bg-tpt18-base px-5 py-16 font-tpt18-sans text-tpt18-ink antialiased" aria-label="Neumorphic pricing tiers">
<div class="grid w-full max-w-5xl justify-items-center gap-11">
<h2 class="text-center text-[clamp(26px,3.8vw,40px)] font-extrabold tracking-[-0.02em]">Choose your calm</h2>
<fieldset class="grid w-full gap-7 md:grid-cols-3">
<legend class="sr-only">Plan</legend>
<label class="group/c flex cursor-pointer flex-col gap-2 rounded-[26px] border-2 border-transparent bg-tpt18-base p-8 shadow-[-9px_-9px_20px_var(--color-tpt18-lite),9px_9px_20px_var(--color-tpt18-shade)] transition-[box-shadow,border-color,translate] duration-300 hover:-translate-y-0.5 has-checked:border-tpt18-acc has-checked:shadow-[-4px_-4px_12px_var(--color-tpt18-lite),4px_4px_12px_var(--color-tpt18-shade)] has-focus-visible:outline-3 has-focus-visible:outline-offset-4 has-focus-visible:outline-tpt18-acc motion-reduce:hover:translate-y-0">
<input type="radio" name="tpt18-plan" value="calm" class="sr-only">
<h3 class="text-[13px] font-extrabold tracking-[0.1em] text-tpt18-mut uppercase">Calm</h3>
<p class="text-[44px] leading-tight font-extrabold tracking-[-0.03em]">$7<small class="text-[15px] font-bold tracking-normal text-tpt18-mut">/mo</small></p>
<ul class="mt-2 mb-6 grid flex-1 content-start gap-2.5 text-sm font-medium text-tpt18-body *:flex *:items-center *:gap-3 *:before:size-2 *:before:rounded-full *:before:bg-tpt18-acc *:before:content-['']"><li>10 sessions / month</li><li>Sleep stories</li></ul>
<span class="grid min-h-12 place-items-center rounded-2xl bg-tpt18-base text-sm font-extrabold text-tpt18-ink shadow-[-5px_-5px_12px_var(--color-tpt18-lite),5px_5px_12px_var(--color-tpt18-shade)] transition-[box-shadow,color,translate] duration-200 group-hover/c:text-tpt18-acc group-active/c:translate-y-px group-active/c:shadow-none group-active/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)] group-has-checked/c:text-tpt18-acc group-has-checked/c:shadow-none group-has-checked/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)]"><span class="group-has-checked/c:hidden">Select</span><span class="hidden group-has-checked/c:inline">✓ Selected</span></span>
</label>
<label class="group/c flex cursor-pointer flex-col gap-2 rounded-[26px] border-2 border-transparent bg-tpt18-base p-8 shadow-[-9px_-9px_20px_var(--color-tpt18-lite),9px_9px_20px_var(--color-tpt18-shade)] transition-[box-shadow,border-color,translate] duration-300 hover:-translate-y-0.5 has-checked:border-tpt18-acc has-checked:shadow-[-4px_-4px_12px_var(--color-tpt18-lite),4px_4px_12px_var(--color-tpt18-shade)] has-focus-visible:outline-3 has-focus-visible:outline-offset-4 has-focus-visible:outline-tpt18-acc motion-reduce:hover:translate-y-0">
<input type="radio" name="tpt18-plan" value="focus" class="sr-only" checked>
<h3 class="text-[13px] font-extrabold tracking-[0.1em] text-tpt18-mut uppercase">Focus</h3>
<p class="text-[44px] leading-tight font-extrabold tracking-[-0.03em]">$14<small class="text-[15px] font-bold tracking-normal text-tpt18-mut">/mo</small></p>
<ul class="mt-2 mb-6 grid flex-1 content-start gap-2.5 text-sm font-medium text-tpt18-body *:flex *:items-center *:gap-3 *:before:size-2 *:before:rounded-full *:before:bg-tpt18-acc *:before:content-['']"><li>Unlimited sessions</li><li>Focus soundscapes</li><li>Mood & streak stats</li></ul>
<span class="grid min-h-12 place-items-center rounded-2xl bg-tpt18-base text-sm font-extrabold text-tpt18-ink shadow-[-5px_-5px_12px_var(--color-tpt18-lite),5px_5px_12px_var(--color-tpt18-shade)] transition-[box-shadow,color,translate] duration-200 group-hover/c:text-tpt18-acc group-active/c:translate-y-px group-active/c:shadow-none group-active/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)] group-has-checked/c:text-tpt18-acc group-has-checked/c:shadow-none group-has-checked/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)]"><span class="group-has-checked/c:hidden">Select</span><span class="hidden group-has-checked/c:inline">✓ Selected</span></span>
</label>
<label class="group/c flex cursor-pointer flex-col gap-2 rounded-[26px] border-2 border-transparent bg-tpt18-base p-8 shadow-[-9px_-9px_20px_var(--color-tpt18-lite),9px_9px_20px_var(--color-tpt18-shade)] transition-[box-shadow,border-color,translate] duration-300 hover:-translate-y-0.5 has-checked:border-tpt18-acc has-checked:shadow-[-4px_-4px_12px_var(--color-tpt18-lite),4px_4px_12px_var(--color-tpt18-shade)] has-focus-visible:outline-3 has-focus-visible:outline-offset-4 has-focus-visible:outline-tpt18-acc motion-reduce:hover:translate-y-0">
<input type="radio" name="tpt18-plan" value="flow" class="sr-only">
<h3 class="text-[13px] font-extrabold tracking-[0.1em] text-tpt18-mut uppercase">Flow</h3>
<p class="text-[44px] leading-tight font-extrabold tracking-[-0.03em]">$21<small class="text-[15px] font-bold tracking-normal text-tpt18-mut">/mo</small></p>
<ul class="mt-2 mb-6 grid flex-1 content-start gap-2.5 text-sm font-medium text-tpt18-body *:flex *:items-center *:gap-3 *:before:size-2 *:before:rounded-full *:before:bg-tpt18-acc *:before:content-['']"><li>Everything in Focus</li><li>Weekly 1:1 coaching</li></ul>
<span class="grid min-h-12 place-items-center rounded-2xl bg-tpt18-base text-sm font-extrabold text-tpt18-ink shadow-[-5px_-5px_12px_var(--color-tpt18-lite),5px_5px_12px_var(--color-tpt18-shade)] transition-[box-shadow,color,translate] duration-200 group-hover/c:text-tpt18-acc group-active/c:translate-y-px group-active/c:shadow-none group-active/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)] group-has-checked/c:text-tpt18-acc group-has-checked/c:shadow-none group-has-checked/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)]"><span class="group-has-checked/c:hidden">Select</span><span class="hidden group-has-checked/c:inline">✓ Selected</span></span>
</label>
</fieldset>
<p aria-hidden="true" class="rounded-full px-4 py-2 font-tpt18-mono text-[11.5px] text-tpt18-mut shadow-[inset_-3px_-3px_8px_var(--color-tpt18-lite),inset_3px_3px_8px_var(--color-tpt18-shade)]">shadow-[light,dark] · has-checked:inset-shadow-[…] · radio cards</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tpt18-base: #e4e9f2;
--color-tpt18-base: oklch(0.93 0.012 260);
--color-tpt18-lite: #f8faff;
--color-tpt18-lite: oklch(0.985 0.005 260);
--color-tpt18-shade: #b9c1d3;
--color-tpt18-shade: oklch(0.8 0.025 262);
--color-tpt18-ink: #262c3c;
--color-tpt18-ink: oklch(0.28 0.03 265);
--color-tpt18-body: #404863;
--color-tpt18-body: oklch(0.4 0.04 265);
--color-tpt18-mut: #535c77;
--color-tpt18-mut: oklch(0.47 0.04 265);
--color-tpt18-acc: #4252c9;
--color-tpt18-acc: oklch(0.5 0.18 268);
--font-tpt18-sans: 'Nunito', system-ui, sans-serif;
--font-tpt18-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities. */
.tpt-18 {
width: 100%;
min-height: 100svh;
}
```How this works
Two shadows, one surface. Cards share the page's exact background; a light shadow up-left and a dark shadow down-right define their geometry. Both colours are @theme tokens derived from the base, so the pair never looks dirty.
shadow-[-5px_-5px_12px_var(--color-tpt18-lite),5px_5px_12px_var(--color-tpt18-shade)]
group-has-checked/c:shadow-none
group-has-checked/c:inset-shadow-[4px_4px_10px_var(--color-tpt18-shade)]Inset is its own layer in v4. inset-shadow-* composes with shadow-* instead of replacing it, so the transition from raised to pressed is a clean crossfade.
Contrast discipline. Text runs far darker than typical neumorphic showcases, and focus uses a solid outline — soft, but WCAG-compliant.
Make it yours
- Surface:
--color-tpt18-base, then re-derive-liteand-shade. - Depth: the shadow offsets (keep the light/dark pair symmetric).
- Accent ring:
--color-tpt18-acc. - Pre-select a different plan: move
checked.
Gotchas — read before shipping
- Never rely on shadows alone for state — the ring and label here are the real indicators.
- The shadow tokens must derive from the base hue, or cards look grubby.
- Radios in one group need the same
name(tpt18-plan).
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Box shadows are universal; :has() drives selection styling.