28 / 36
All 36 in 36 Tailwind CSS Modals
- 01Tailwind Login / Signup Modal with Native Dialog & Starting-Style
- 02Tailwind Password Reset Modal with In-Place Success State
- 03Tailwind Age Verification Gate with :has() and Zero JavaScript
- 04Tailwind Paywall Modal with Masked Article Fade
- 05Tailwind Product Quick-View Modal with has-checked Variant Chips
- 06Tailwind Shopping Cart Slide-Over Drawer with Dialog
- 07Tailwind Timed Newsletter Promo Popup with Frequency Cap
- 08Tailwind Size Guide Overlay with Zero-JS cm/inch Toggle
- 09Tailwind Zero-JS Lightbox Gallery with the target Variant
- 10Tailwind Video Trailer Modal with Aspect-Locked Iframe
- 11Tailwind Destructive Delete Modal with Type-to-Confirm Gate
- 12Tailwind Success / Error Result Modal with Drawn SVG Icon
- 13Tailwind GDPR Cookie Consent Banner with details Disclosure
- 14Tailwind Multi-Step Onboarding Modal with data-step Variants
- 15Tailwind Filter & Sort Drawer that Becomes a Bottom Sheet
- 16Tailwind Contact Support Modal with Live Validation
- 17Tailwind Tabbed Settings Modal with Zero-JS Radio Tabs
- 18Tailwind Command Palette Modal with ⌘K and Roving Selection
- 19Tailwind Confirm Delete Modal with Corner Slide-and-Spin Entrance
- 20Tailwind Login Modal with Staggered Blur-In Content Drop
- 21Tailwind Signup Modal with 3D Origami Fan Deploy Panels
- 22Tailwind Payment Checkout Modal with 3D Paper Fold Entrance
- 23Tailwind HIPAA Patient Consent Modal with Scroll-Gated Agreement
- 24Tailwind Age Verification Modal with Clip-Path Iris Aperture
- 25Tailwind GDPR Cookie Preferences Modal with Unfolding Category Rows
- 26Tailwind Video Lightbox Modal with Chromatic Glitch Materialize
- 27Tailwind Image Gallery Lightbox with Clip-Path Diamond Reveal
- 28Tailwind Add to Cart Confirmation with SVG Liquid Morph Entrance
- 29Tailwind Product Quick View Modal with Elastic Button Morph
- 30Tailwind Newsletter Signup Modal with Depth-Layer Parallax Entrance
- 31Tailwind Exit-Intent Discount Modal with Live Countdown and Coupon Copy
- 32Tailwind Restaurant Reservation Modal with Cascading Fan Panels
- 33Tailwind Order Success Modal with CSS-Only Confetti Burst
- 34Tailwind SaaS Onboarding Welcome Modal with Cascading Blur-In
- 35Tailwind Terms of Service Modal with Letter-Slide and Consent Gate
- 36Tailwind Password Reset Modal with Enumeration-Safe Confirmation
Tailwind Add to Cart Confirmation with SVG Liquid Morph Entrance
An e-commerce confirmation that blobs into existence: an SVG goo filter plus a 0.6→1 spring, with the filter dropped the instant the card settles so text renders crisp. The port carries the filter as [filter:url(#tw-10-goo)] and switches it off with open:[filter:none], keeping the whole effect in the class list.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tw-10 w-full min-h-svh grid place-items-center bg-tw10-bg font-tw10-sans px-6 py-10">
<svg width="0" height="0" class="absolute" aria-hidden="true">
<defs>
<filter id="tw-10-goo">
<feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur"/>
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="goo"/>
<feComposite in="SourceGraphic" in2="goo" operator="atop"/>
</filter>
</defs>
</svg>
<button type="button" data-tw10-open class="inline-flex items-center justify-center min-h-11 px-7 rounded-[9px] border-0 bg-tw10-brand text-[15px] font-bold text-white shadow-[0_10px_22px_-6px_var(--color-tw10-glow)] cursor-pointer transition-[background-color,translate] duration-200 hover:bg-tw10-branddark hover:-translate-y-px focus-visible:outline-2 focus-visible:outline-tw10-brand focus-visible:outline-offset-2 motion-reduce:hover:translate-y-0">Add to cart · $89</button>
<dialog id="tw-10-dlg" aria-labelledby="tw-10-title" class="m-auto w-[min(460px,94vw)] p-0 border-0 rounded-2xl text-left text-tw10-ink bg-tw10-card shadow-[0_40px_60px_-20px_#1e293b59] opacity-0 scale-[0.6] [filter:url(#tw-10-goo)] transition-[opacity,scale,filter,overlay,display] duration-500 ease-[cubic-bezier(.34,1.56,.64,1)] [transition-delay:0ms,0ms,500ms] transition-discrete open:opacity-100 open:scale-100 open:[filter:none] open:starting:opacity-0 open:starting:scale-[0.6] open:backdrop:bg-tw10-scrim open:backdrop:backdrop-blur-[6px] motion-reduce:transition-[opacity] motion-reduce:duration-150 motion-reduce:scale-100 motion-reduce:[filter:none]">
<div class="relative px-7 pt-7 pb-6">
<button type="button" data-tw10-close aria-label="Close dialog" class="absolute top-3.5 right-3.5 grid place-items-center size-9 rounded-lg border-0 bg-transparent text-tw10-sub text-[15px] cursor-pointer transition-colors duration-200 hover:bg-tw10-well2 hover:text-tw10-ink focus-visible:outline-2 focus-visible:outline-tw10-brand focus-visible:outline-offset-2">✕</button>
<p class="inline-flex items-center gap-2 mb-3 py-1 pl-1.5 pr-3 rounded-full bg-tw10-goodtint text-xs font-bold uppercase tracking-wide text-tw10-good">
<span aria-hidden="true" class="grid place-items-center size-5 rounded-full bg-tw10-good text-[11px] text-white">✓</span>Added to cart
</p>
<h2 id="tw-10-title" class="mb-[18px] text-[1.35rem] font-extrabold tracking-[-0.015em] leading-tight">You're all set</h2>
<div class="flex items-center gap-3.5 mb-3.5 p-3.5 rounded-xl border border-tw10-line bg-tw10-well">
<span aria-hidden="true" data-tw10-thumb class="shrink-0 grid place-items-center size-18 rounded-[9px] text-[32px]">👟</span>
<div class="flex-1 min-w-0">
<p class="mb-0.5 text-sm font-bold">Terra Runner · Slate / Coral</p>
<p class="mb-0.5 text-xs text-tw10-sub">Size 10 · Qty 1</p>
<p class="text-[15px] font-extrabold text-tw10-brand tabular-nums">$89.00</p>
</div>
</div>
<div class="mb-4 px-4 py-3.5 rounded-[9px] border border-tw10-line">
<div class="flex justify-between items-center text-sm"><span>Cart subtotal (2 items)</span><strong class="font-extrabold tabular-nums">$168.00</strong></div>
<div class="flex justify-between items-center mt-2 pt-2 border-t border-tw10-line text-sm font-semibold text-tw10-good"><span>Free shipping unlocked</span><span aria-hidden="true">🎉</span></div>
</div>
<p aria-live="polite" class="sr-only">Terra Runner added to cart. Cart subtotal is $168.00 for 2 items.</p>
<div class="grid grid-cols-2 gap-2.5">
<button type="button" data-tw10-close class="min-h-11 rounded-[9px] border-0 bg-tw10-well2 text-sm font-bold text-tw10-ink cursor-pointer transition-[background-color,translate] duration-150 hover:bg-tw10-well3 active:translate-y-px focus-visible:outline-2 focus-visible:outline-tw10-ink focus-visible:outline-offset-2 motion-reduce:active:translate-y-0">Continue shopping</button>
<button type="button" autofocus data-tw10-close class="min-h-11 rounded-[9px] border-0 bg-tw10-brand text-sm font-bold text-white cursor-pointer transition-[background-color,translate] duration-150 hover:bg-tw10-branddark active:translate-y-px focus-visible:outline-2 focus-visible:outline-tw10-brand focus-visible:outline-offset-2 motion-reduce:active:translate-y-0">View cart →</button>
</div>
</div>
</dialog>
</section><section class="tw-10 w-full min-h-svh grid place-items-center bg-tw10-bg font-tw10-sans px-6 py-10">
<svg width="0" height="0" class="absolute" aria-hidden="true">
<defs>
<filter id="tw-10-goo">
<feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur"/>
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="goo"/>
<feComposite in="SourceGraphic" in2="goo" operator="atop"/>
</filter>
</defs>
</svg>
<button type="button" data-tw10-open class="inline-flex items-center justify-center min-h-11 px-7 rounded-[9px] border-0 bg-tw10-brand text-[15px] font-bold text-white shadow-[0_10px_22px_-6px_var(--color-tw10-glow)] cursor-pointer transition-[background-color,translate] duration-200 hover:bg-tw10-branddark hover:-translate-y-px focus-visible:outline-2 focus-visible:outline-tw10-brand focus-visible:outline-offset-2 motion-reduce:hover:translate-y-0">Add to cart · $89</button>
<dialog id="tw-10-dlg" aria-labelledby="tw-10-title" class="m-auto w-[min(460px,94vw)] p-0 border-0 rounded-2xl text-left text-tw10-ink bg-tw10-card shadow-[0_40px_60px_-20px_#1e293b59] opacity-0 scale-[0.6] [filter:url(#tw-10-goo)] transition-[opacity,scale,filter,overlay,display] duration-500 ease-[cubic-bezier(.34,1.56,.64,1)] [transition-delay:0ms,0ms,500ms] transition-discrete open:opacity-100 open:scale-100 open:[filter:none] open:starting:opacity-0 open:starting:scale-[0.6] open:backdrop:bg-tw10-scrim open:backdrop:backdrop-blur-[6px] motion-reduce:transition-[opacity] motion-reduce:duration-150 motion-reduce:scale-100 motion-reduce:[filter:none]">
<div class="relative px-7 pt-7 pb-6">
<button type="button" data-tw10-close aria-label="Close dialog" class="absolute top-3.5 right-3.5 grid place-items-center size-9 rounded-lg border-0 bg-transparent text-tw10-sub text-[15px] cursor-pointer transition-colors duration-200 hover:bg-tw10-well2 hover:text-tw10-ink focus-visible:outline-2 focus-visible:outline-tw10-brand focus-visible:outline-offset-2">✕</button>
<p class="inline-flex items-center gap-2 mb-3 py-1 pl-1.5 pr-3 rounded-full bg-tw10-goodtint text-xs font-bold uppercase tracking-wide text-tw10-good">
<span aria-hidden="true" class="grid place-items-center size-5 rounded-full bg-tw10-good text-[11px] text-white">✓</span>Added to cart
</p>
<h2 id="tw-10-title" class="mb-[18px] text-[1.35rem] font-extrabold tracking-[-0.015em] leading-tight">You're all set</h2>
<div class="flex items-center gap-3.5 mb-3.5 p-3.5 rounded-xl border border-tw10-line bg-tw10-well">
<span aria-hidden="true" data-tw10-thumb class="shrink-0 grid place-items-center size-18 rounded-[9px] text-[32px]">👟</span>
<div class="flex-1 min-w-0">
<p class="mb-0.5 text-sm font-bold">Terra Runner · Slate / Coral</p>
<p class="mb-0.5 text-xs text-tw10-sub">Size 10 · Qty 1</p>
<p class="text-[15px] font-extrabold text-tw10-brand tabular-nums">$89.00</p>
</div>
</div>
<div class="mb-4 px-4 py-3.5 rounded-[9px] border border-tw10-line">
<div class="flex justify-between items-center text-sm"><span>Cart subtotal (2 items)</span><strong class="font-extrabold tabular-nums">$168.00</strong></div>
<div class="flex justify-between items-center mt-2 pt-2 border-t border-tw10-line text-sm font-semibold text-tw10-good"><span>Free shipping unlocked</span><span aria-hidden="true">🎉</span></div>
</div>
<p aria-live="polite" class="sr-only">Terra Runner added to cart. Cart subtotal is $168.00 for 2 items.</p>
<div class="grid grid-cols-2 gap-2.5">
<button type="button" data-tw10-close class="min-h-11 rounded-[9px] border-0 bg-tw10-well2 text-sm font-bold text-tw10-ink cursor-pointer transition-[background-color,translate] duration-150 hover:bg-tw10-well3 active:translate-y-px focus-visible:outline-2 focus-visible:outline-tw10-ink focus-visible:outline-offset-2 motion-reduce:active:translate-y-0">Continue shopping</button>
<button type="button" autofocus data-tw10-close class="min-h-11 rounded-[9px] border-0 bg-tw10-brand text-sm font-bold text-white cursor-pointer transition-[background-color,translate] duration-150 hover:bg-tw10-branddark active:translate-y-px focus-visible:outline-2 focus-visible:outline-tw10-brand focus-visible:outline-offset-2 motion-reduce:active:translate-y-0">View cart →</button>
</div>
</div>
</dialog>
</section>@import "tailwindcss";
@theme {
--color-tw10-bg: #fdf7f4;
--color-tw10-bg: oklch(0.97 0.012 40);
--color-tw10-card: #ffffff;
--color-tw10-card: oklch(1 0 0);
--color-tw10-well: #fafbfc;
--color-tw10-well: oklch(0.98 0.003 255);
--color-tw10-well2: #f1f5f9;
--color-tw10-well2: oklch(0.96 0.006 250);
--color-tw10-well3: #e2e8f0;
--color-tw10-well3: oklch(0.92 0.01 250);
--color-tw10-ink: #1e293b;
--color-tw10-ink: oklch(0.3 0.03 258);
--color-tw10-sub: #55657c;
--color-tw10-sub: oklch(0.5 0.03 255);
--color-tw10-line: #eef1f5;
--color-tw10-line: oklch(0.95 0.005 255);
--color-tw10-brand: #e05252;
--color-tw10-brand: oklch(0.62 0.17 22);
--color-tw10-branddark: #c23e3e;
--color-tw10-branddark: oklch(0.55 0.17 22);
--color-tw10-glow: #ff6b6b80;
--color-tw10-glow: oklch(0.7 0.17 22 / 0.5);
--color-tw10-good: #047857;
--color-tw10-good: oklch(0.52 0.12 164);
--color-tw10-goodtint: #0596691f;
--color-tw10-goodtint: oklch(0.63 0.14 163 / 0.12);
--color-tw10-scrim: #1e293b80;
--color-tw10-scrim: oklch(0.3 0.03 258 / 0.5);
--font-tw10-sans: 'Outfit', system-ui, sans-serif;
}
.tw-10 {
width: 100%;
}
/* Placeholder product swatch — swap for a real <img>. */
.tw-10 [data-tw10-thumb] {
background-image: linear-gradient(135deg, oklch(0.93 0.04 350), oklch(0.95 0.06 90));
}
#tw-10-dlg::backdrop {
background: oklch(0.3 0.03 258 / 0);
backdrop-filter: blur(0px);
transition: background .25s ease, backdrop-filter .25s ease, overlay .25s allow-discrete, display .25s allow-discrete;
}
@starting-style {
#tw-10-dlg[open]::backdrop {
background: oklch(0.3 0.03 258 / 0);
backdrop-filter: blur(0px);
}
}
@media (prefers-reduced-motion: reduce) {
#tw-10-dlg::backdrop {
transition-duration: .001s;
}
}@import "tailwindcss";
@theme {
--color-tw10-bg: #fdf7f4;
--color-tw10-bg: oklch(0.97 0.012 40);
--color-tw10-card: #ffffff;
--color-tw10-card: oklch(1 0 0);
--color-tw10-well: #fafbfc;
--color-tw10-well: oklch(0.98 0.003 255);
--color-tw10-well2: #f1f5f9;
--color-tw10-well2: oklch(0.96 0.006 250);
--color-tw10-well3: #e2e8f0;
--color-tw10-well3: oklch(0.92 0.01 250);
--color-tw10-ink: #1e293b;
--color-tw10-ink: oklch(0.3 0.03 258);
--color-tw10-sub: #55657c;
--color-tw10-sub: oklch(0.5 0.03 255);
--color-tw10-line: #eef1f5;
--color-tw10-line: oklch(0.95 0.005 255);
--color-tw10-brand: #e05252;
--color-tw10-brand: oklch(0.62 0.17 22);
--color-tw10-branddark: #c23e3e;
--color-tw10-branddark: oklch(0.55 0.17 22);
--color-tw10-glow: #ff6b6b80;
--color-tw10-glow: oklch(0.7 0.17 22 / 0.5);
--color-tw10-good: #047857;
--color-tw10-good: oklch(0.52 0.12 164);
--color-tw10-goodtint: #0596691f;
--color-tw10-goodtint: oklch(0.63 0.14 163 / 0.12);
--color-tw10-scrim: #1e293b80;
--color-tw10-scrim: oklch(0.3 0.03 258 / 0.5);
--font-tw10-sans: 'Outfit', system-ui, sans-serif;
}
.tw-10 {
width: 100%;
}
/* Placeholder product swatch — swap for a real <img>. */
.tw-10 [data-tw10-thumb] {
background-image: linear-gradient(135deg, oklch(0.93 0.04 350), oklch(0.95 0.06 90));
}
#tw-10-dlg::backdrop {
background: oklch(0.3 0.03 258 / 0);
backdrop-filter: blur(0px);
transition: background .25s ease, backdrop-filter .25s ease, overlay .25s allow-discrete, display .25s allow-discrete;
}
@starting-style {
#tw-10-dlg[open]::backdrop {
background: oklch(0.3 0.03 258 / 0);
backdrop-filter: blur(0px);
}
}
@media (prefers-reduced-motion: reduce) {
#tw-10-dlg::backdrop {
transition-duration: .001s;
}
}(function () {
var root = document.querySelector('.tw-10');
var dlg = root.querySelector('#tw-10-dlg');
var card = dlg.firstElementChild;
root.querySelector('[data-tw10-open]').addEventListener('click', function () { dlg.showModal(); });
root.querySelectorAll('[data-tw10-close]').forEach(function (b) {
b.addEventListener('click', function () { dlg.close(); });
});
dlg.addEventListener('click', function (e) {
var r = card.getBoundingClientRect();
if (e.clientX < r.left || e.clientX > r.right || e.clientY < r.top || e.clientY > r.bottom) dlg.close();
});
})();(function () {
var root = document.querySelector('.tw-10');
var dlg = root.querySelector('#tw-10-dlg');
var card = dlg.firstElementChild;
root.querySelector('[data-tw10-open]').addEventListener('click', function () { dlg.showModal(); });
root.querySelectorAll('[data-tw10-close]').forEach(function (b) {
b.addEventListener('click', function () { dlg.close(); });
});
dlg.addEventListener('click', function (e) {
var r = card.getBoundingClientRect();
if (e.clientX < r.left || e.clientX > r.right || e.clientY < r.top || e.clientY > r.bottom) dlg.close();
});
})();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-tw10-bg: #fdf7f4;
--color-tw10-bg: oklch(0.97 0.012 40);
--color-tw10-card: #ffffff;
--color-tw10-card: oklch(1 0 0);
--color-tw10-well: #fafbfc;
--color-tw10-well: oklch(0.98 0.003 255);
--color-tw10-well2: #f1f5f9;
--color-tw10-well2: oklch(0.96 0.006 250);
--color-tw10-well3: #e2e8f0;
--color-tw10-well3: oklch(0.92 0.01 250);
--color-tw10-ink: #1e293b;
--color-tw10-ink: oklch(0.3 0.03 258);
--color-tw10-sub: #55657c;
--color-tw10-sub: oklch(0.5 0.03 255);
--color-tw10-line: #eef1f5;
--color-tw10-line: oklch(0.95 0.005 255);
--color-tw10-brand: #e05252;
--color-tw10-brand: oklch(0.62 0.17 22);
--color-tw10-branddark: #c23e3e;
--color-tw10-branddark: oklch(0.55 0.17 22);
--color-tw10-glow: #ff6b6b80;
--color-tw10-glow: oklch(0.7 0.17 22 / 0.5);
--color-tw10-good: #047857;
--color-tw10-good: oklch(0.52 0.12 164);
--color-tw10-goodtint: #0596691f;
--color-tw10-goodtint: oklch(0.63 0.14 163 / 0.12);
--color-tw10-scrim: #1e293b80;
--color-tw10-scrim: oklch(0.3 0.03 258 / 0.5);
--font-tw10-sans: 'Outfit', system-ui, sans-serif;
}
.tw-10 {
width: 100%;
}
/* Placeholder product swatch — swap for a real <img>. */
.tw-10 [data-tw10-thumb] {
background-image: linear-gradient(135deg, oklch(0.93 0.04 350), oklch(0.95 0.06 90));
}
#tw-10-dlg::backdrop {
background: oklch(0.3 0.03 258 / 0);
backdrop-filter: blur(0px);
transition: background .25s ease, backdrop-filter .25s ease, overlay .25s allow-discrete, display .25s allow-discrete;
}
@starting-style {
#tw-10-dlg[open]::backdrop {
background: oklch(0.3 0.03 258 / 0);
backdrop-filter: blur(0px);
}
}
@media (prefers-reduced-motion: reduce) {
#tw-10-dlg::backdrop {
transition-duration: .001s;
}
}@import "tailwindcss";
@theme {
--color-tw10-bg: #fdf7f4;
--color-tw10-bg: oklch(0.97 0.012 40);
--color-tw10-card: #ffffff;
--color-tw10-card: oklch(1 0 0);
--color-tw10-well: #fafbfc;
--color-tw10-well: oklch(0.98 0.003 255);
--color-tw10-well2: #f1f5f9;
--color-tw10-well2: oklch(0.96 0.006 250);
--color-tw10-well3: #e2e8f0;
--color-tw10-well3: oklch(0.92 0.01 250);
--color-tw10-ink: #1e293b;
--color-tw10-ink: oklch(0.3 0.03 258);
--color-tw10-sub: #55657c;
--color-tw10-sub: oklch(0.5 0.03 255);
--color-tw10-line: #eef1f5;
--color-tw10-line: oklch(0.95 0.005 255);
--color-tw10-brand: #e05252;
--color-tw10-brand: oklch(0.62 0.17 22);
--color-tw10-branddark: #c23e3e;
--color-tw10-branddark: oklch(0.55 0.17 22);
--color-tw10-glow: #ff6b6b80;
--color-tw10-glow: oklch(0.7 0.17 22 / 0.5);
--color-tw10-good: #047857;
--color-tw10-good: oklch(0.52 0.12 164);
--color-tw10-goodtint: #0596691f;
--color-tw10-goodtint: oklch(0.63 0.14 163 / 0.12);
--color-tw10-scrim: #1e293b80;
--color-tw10-scrim: oklch(0.3 0.03 258 / 0.5);
--font-tw10-sans: 'Outfit', system-ui, sans-serif;
}
.tw-10 {
width: 100%;
}
/* Placeholder product swatch — swap for a real <img>. */
.tw-10 [data-tw10-thumb] {
background-image: linear-gradient(135deg, oklch(0.93 0.04 350), oklch(0.95 0.06 90));
}
#tw-10-dlg::backdrop {
background: oklch(0.3 0.03 258 / 0);
backdrop-filter: blur(0px);
transition: background .25s ease, backdrop-filter .25s ease, overlay .25s allow-discrete, display .25s allow-discrete;
}
@starting-style {
#tw-10-dlg[open]::backdrop {
background: oklch(0.3 0.03 258 / 0);
backdrop-filter: blur(0px);
}
}
@media (prefers-reduced-motion: reduce) {
#tw-10-dlg::backdrop {
transition-duration: .001s;
}
}<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 filter's on and off states are both utilities, and the per-property
[transition-delay:0ms,0ms,500ms]is what sequences them — the original needed a hand-written three-part transition declaration to say the same thing. - The SVG filter definition stays in the markup, where it belongs: it is a document resource, not a style.
- The auto-dismiss from the original is deliberately not ported. A timed close with no pause control is an accessibility failure, and the confirmation is cheap to dismiss manually.
- The coral was darkened from
#ff6b6bso white button text clears 4.5:1; the original's lighter coral failed on the primary action. - UI change from the CSS original: both actions are 44px in a two-column grid, prices are
tabular-nums, and ansr-onlylive region announces what the filter cannot.
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 Add to Cart Confirmation with SVG Liquid Morph Entrance
Source: https://codefronts.com/components/tailwind-css-modals/tailwind-add-to-cart-modal-liquid-morph-metaball/
An e-commerce confirmation that blobs into existence: an SVG goo filter plus a 0.6→1 spring, with the filter dropped the instant the card settles so text renders crisp. The port carries the filter as [filter:url(#tw-10-goo)] and switches it off with open:[filter:none], keeping the whole effect in the class list.
## HTML
```html
<section class="tw-10 w-full min-h-svh grid place-items-center bg-tw10-bg font-tw10-sans px-6 py-10">
<svg width="0" height="0" class="absolute" aria-hidden="true">
<defs>
<filter id="tw-10-goo">
<feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur"/>
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="goo"/>
<feComposite in="SourceGraphic" in2="goo" operator="atop"/>
</filter>
</defs>
</svg>
<button type="button" data-tw10-open class="inline-flex items-center justify-center min-h-11 px-7 rounded-[9px] border-0 bg-tw10-brand text-[15px] font-bold text-white shadow-[0_10px_22px_-6px_var(--color-tw10-glow)] cursor-pointer transition-[background-color,translate] duration-200 hover:bg-tw10-branddark hover:-translate-y-px focus-visible:outline-2 focus-visible:outline-tw10-brand focus-visible:outline-offset-2 motion-reduce:hover:translate-y-0">Add to cart · $89</button>
<dialog id="tw-10-dlg" aria-labelledby="tw-10-title" class="m-auto w-[min(460px,94vw)] p-0 border-0 rounded-2xl text-left text-tw10-ink bg-tw10-card shadow-[0_40px_60px_-20px_#1e293b59] opacity-0 scale-[0.6] [filter:url(#tw-10-goo)] transition-[opacity,scale,filter,overlay,display] duration-500 ease-[cubic-bezier(.34,1.56,.64,1)] [transition-delay:0ms,0ms,500ms] transition-discrete open:opacity-100 open:scale-100 open:[filter:none] open:starting:opacity-0 open:starting:scale-[0.6] open:backdrop:bg-tw10-scrim open:backdrop:backdrop-blur-[6px] motion-reduce:transition-[opacity] motion-reduce:duration-150 motion-reduce:scale-100 motion-reduce:[filter:none]">
<div class="relative px-7 pt-7 pb-6">
<button type="button" data-tw10-close aria-label="Close dialog" class="absolute top-3.5 right-3.5 grid place-items-center size-9 rounded-lg border-0 bg-transparent text-tw10-sub text-[15px] cursor-pointer transition-colors duration-200 hover:bg-tw10-well2 hover:text-tw10-ink focus-visible:outline-2 focus-visible:outline-tw10-brand focus-visible:outline-offset-2">✕</button>
<p class="inline-flex items-center gap-2 mb-3 py-1 pl-1.5 pr-3 rounded-full bg-tw10-goodtint text-xs font-bold uppercase tracking-wide text-tw10-good">
<span aria-hidden="true" class="grid place-items-center size-5 rounded-full bg-tw10-good text-[11px] text-white">✓</span>Added to cart
</p>
<h2 id="tw-10-title" class="mb-[18px] text-[1.35rem] font-extrabold tracking-[-0.015em] leading-tight">You're all set</h2>
<div class="flex items-center gap-3.5 mb-3.5 p-3.5 rounded-xl border border-tw10-line bg-tw10-well">
<span aria-hidden="true" data-tw10-thumb class="shrink-0 grid place-items-center size-18 rounded-[9px] text-[32px]">👟</span>
<div class="flex-1 min-w-0">
<p class="mb-0.5 text-sm font-bold">Terra Runner · Slate / Coral</p>
<p class="mb-0.5 text-xs text-tw10-sub">Size 10 · Qty 1</p>
<p class="text-[15px] font-extrabold text-tw10-brand tabular-nums">$89.00</p>
</div>
</div>
<div class="mb-4 px-4 py-3.5 rounded-[9px] border border-tw10-line">
<div class="flex justify-between items-center text-sm"><span>Cart subtotal (2 items)</span><strong class="font-extrabold tabular-nums">$168.00</strong></div>
<div class="flex justify-between items-center mt-2 pt-2 border-t border-tw10-line text-sm font-semibold text-tw10-good"><span>Free shipping unlocked</span><span aria-hidden="true">🎉</span></div>
</div>
<p aria-live="polite" class="sr-only">Terra Runner added to cart. Cart subtotal is $168.00 for 2 items.</p>
<div class="grid grid-cols-2 gap-2.5">
<button type="button" data-tw10-close class="min-h-11 rounded-[9px] border-0 bg-tw10-well2 text-sm font-bold text-tw10-ink cursor-pointer transition-[background-color,translate] duration-150 hover:bg-tw10-well3 active:translate-y-px focus-visible:outline-2 focus-visible:outline-tw10-ink focus-visible:outline-offset-2 motion-reduce:active:translate-y-0">Continue shopping</button>
<button type="button" autofocus data-tw10-close class="min-h-11 rounded-[9px] border-0 bg-tw10-brand text-sm font-bold text-white cursor-pointer transition-[background-color,translate] duration-150 hover:bg-tw10-branddark active:translate-y-px focus-visible:outline-2 focus-visible:outline-tw10-brand focus-visible:outline-offset-2 motion-reduce:active:translate-y-0">View cart →</button>
</div>
</div>
</dialog>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tw10-bg: #fdf7f4;
--color-tw10-bg: oklch(0.97 0.012 40);
--color-tw10-card: #ffffff;
--color-tw10-card: oklch(1 0 0);
--color-tw10-well: #fafbfc;
--color-tw10-well: oklch(0.98 0.003 255);
--color-tw10-well2: #f1f5f9;
--color-tw10-well2: oklch(0.96 0.006 250);
--color-tw10-well3: #e2e8f0;
--color-tw10-well3: oklch(0.92 0.01 250);
--color-tw10-ink: #1e293b;
--color-tw10-ink: oklch(0.3 0.03 258);
--color-tw10-sub: #55657c;
--color-tw10-sub: oklch(0.5 0.03 255);
--color-tw10-line: #eef1f5;
--color-tw10-line: oklch(0.95 0.005 255);
--color-tw10-brand: #e05252;
--color-tw10-brand: oklch(0.62 0.17 22);
--color-tw10-branddark: #c23e3e;
--color-tw10-branddark: oklch(0.55 0.17 22);
--color-tw10-glow: #ff6b6b80;
--color-tw10-glow: oklch(0.7 0.17 22 / 0.5);
--color-tw10-good: #047857;
--color-tw10-good: oklch(0.52 0.12 164);
--color-tw10-goodtint: #0596691f;
--color-tw10-goodtint: oklch(0.63 0.14 163 / 0.12);
--color-tw10-scrim: #1e293b80;
--color-tw10-scrim: oklch(0.3 0.03 258 / 0.5);
--font-tw10-sans: 'Outfit', system-ui, sans-serif;
}
.tw-10 {
width: 100%;
}
/* Placeholder product swatch — swap for a real <img>. */
.tw-10 [data-tw10-thumb] {
background-image: linear-gradient(135deg, oklch(0.93 0.04 350), oklch(0.95 0.06 90));
}
#tw-10-dlg::backdrop {
background: oklch(0.3 0.03 258 / 0);
backdrop-filter: blur(0px);
transition: background .25s ease, backdrop-filter .25s ease, overlay .25s allow-discrete, display .25s allow-discrete;
}
@starting-style {
#tw-10-dlg[open]::backdrop {
background: oklch(0.3 0.03 258 / 0);
backdrop-filter: blur(0px);
}
}
@media (prefers-reduced-motion: reduce) {
#tw-10-dlg::backdrop {
transition-duration: .001s;
}
}
```
## JavaScript
```js
(function () {
var root = document.querySelector('.tw-10');
var dlg = root.querySelector('#tw-10-dlg');
var card = dlg.firstElementChild;
root.querySelector('[data-tw10-open]').addEventListener('click', function () { dlg.showModal(); });
root.querySelectorAll('[data-tw10-close]').forEach(function (b) {
b.addEventListener('click', function () { dlg.close(); });
});
dlg.addEventListener('click', function (e) {
var r = card.getBoundingClientRect();
if (e.clientX < r.left || e.clientX > r.right || e.clientY < r.top || e.clientY > r.bottom) dlg.close();
});
})();
```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 Add to Cart Confirmation with SVG Liquid Morph Entrance
Source: https://codefronts.com/components/tailwind-css-modals/tailwind-add-to-cart-modal-liquid-morph-metaball/
An e-commerce confirmation that blobs into existence: an SVG goo filter plus a 0.6→1 spring, with the filter dropped the instant the card settles so text renders crisp. The port carries the filter as [filter:url(#tw-10-goo)] and switches it off with open:[filter:none], keeping the whole effect in the class list.
## HTML
```html
<section class="tw-10 w-full min-h-svh grid place-items-center bg-tw10-bg font-tw10-sans px-6 py-10">
<svg width="0" height="0" class="absolute" aria-hidden="true">
<defs>
<filter id="tw-10-goo">
<feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur"/>
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="goo"/>
<feComposite in="SourceGraphic" in2="goo" operator="atop"/>
</filter>
</defs>
</svg>
<button type="button" data-tw10-open class="inline-flex items-center justify-center min-h-11 px-7 rounded-[9px] border-0 bg-tw10-brand text-[15px] font-bold text-white shadow-[0_10px_22px_-6px_var(--color-tw10-glow)] cursor-pointer transition-[background-color,translate] duration-200 hover:bg-tw10-branddark hover:-translate-y-px focus-visible:outline-2 focus-visible:outline-tw10-brand focus-visible:outline-offset-2 motion-reduce:hover:translate-y-0">Add to cart · $89</button>
<dialog id="tw-10-dlg" aria-labelledby="tw-10-title" class="m-auto w-[min(460px,94vw)] p-0 border-0 rounded-2xl text-left text-tw10-ink bg-tw10-card shadow-[0_40px_60px_-20px_#1e293b59] opacity-0 scale-[0.6] [filter:url(#tw-10-goo)] transition-[opacity,scale,filter,overlay,display] duration-500 ease-[cubic-bezier(.34,1.56,.64,1)] [transition-delay:0ms,0ms,500ms] transition-discrete open:opacity-100 open:scale-100 open:[filter:none] open:starting:opacity-0 open:starting:scale-[0.6] open:backdrop:bg-tw10-scrim open:backdrop:backdrop-blur-[6px] motion-reduce:transition-[opacity] motion-reduce:duration-150 motion-reduce:scale-100 motion-reduce:[filter:none]">
<div class="relative px-7 pt-7 pb-6">
<button type="button" data-tw10-close aria-label="Close dialog" class="absolute top-3.5 right-3.5 grid place-items-center size-9 rounded-lg border-0 bg-transparent text-tw10-sub text-[15px] cursor-pointer transition-colors duration-200 hover:bg-tw10-well2 hover:text-tw10-ink focus-visible:outline-2 focus-visible:outline-tw10-brand focus-visible:outline-offset-2">✕</button>
<p class="inline-flex items-center gap-2 mb-3 py-1 pl-1.5 pr-3 rounded-full bg-tw10-goodtint text-xs font-bold uppercase tracking-wide text-tw10-good">
<span aria-hidden="true" class="grid place-items-center size-5 rounded-full bg-tw10-good text-[11px] text-white">✓</span>Added to cart
</p>
<h2 id="tw-10-title" class="mb-[18px] text-[1.35rem] font-extrabold tracking-[-0.015em] leading-tight">You're all set</h2>
<div class="flex items-center gap-3.5 mb-3.5 p-3.5 rounded-xl border border-tw10-line bg-tw10-well">
<span aria-hidden="true" data-tw10-thumb class="shrink-0 grid place-items-center size-18 rounded-[9px] text-[32px]">👟</span>
<div class="flex-1 min-w-0">
<p class="mb-0.5 text-sm font-bold">Terra Runner · Slate / Coral</p>
<p class="mb-0.5 text-xs text-tw10-sub">Size 10 · Qty 1</p>
<p class="text-[15px] font-extrabold text-tw10-brand tabular-nums">$89.00</p>
</div>
</div>
<div class="mb-4 px-4 py-3.5 rounded-[9px] border border-tw10-line">
<div class="flex justify-between items-center text-sm"><span>Cart subtotal (2 items)</span><strong class="font-extrabold tabular-nums">$168.00</strong></div>
<div class="flex justify-between items-center mt-2 pt-2 border-t border-tw10-line text-sm font-semibold text-tw10-good"><span>Free shipping unlocked</span><span aria-hidden="true">🎉</span></div>
</div>
<p aria-live="polite" class="sr-only">Terra Runner added to cart. Cart subtotal is $168.00 for 2 items.</p>
<div class="grid grid-cols-2 gap-2.5">
<button type="button" data-tw10-close class="min-h-11 rounded-[9px] border-0 bg-tw10-well2 text-sm font-bold text-tw10-ink cursor-pointer transition-[background-color,translate] duration-150 hover:bg-tw10-well3 active:translate-y-px focus-visible:outline-2 focus-visible:outline-tw10-ink focus-visible:outline-offset-2 motion-reduce:active:translate-y-0">Continue shopping</button>
<button type="button" autofocus data-tw10-close class="min-h-11 rounded-[9px] border-0 bg-tw10-brand text-sm font-bold text-white cursor-pointer transition-[background-color,translate] duration-150 hover:bg-tw10-branddark active:translate-y-px focus-visible:outline-2 focus-visible:outline-tw10-brand focus-visible:outline-offset-2 motion-reduce:active:translate-y-0">View cart →</button>
</div>
</div>
</dialog>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tw10-bg: #fdf7f4;
--color-tw10-bg: oklch(0.97 0.012 40);
--color-tw10-card: #ffffff;
--color-tw10-card: oklch(1 0 0);
--color-tw10-well: #fafbfc;
--color-tw10-well: oklch(0.98 0.003 255);
--color-tw10-well2: #f1f5f9;
--color-tw10-well2: oklch(0.96 0.006 250);
--color-tw10-well3: #e2e8f0;
--color-tw10-well3: oklch(0.92 0.01 250);
--color-tw10-ink: #1e293b;
--color-tw10-ink: oklch(0.3 0.03 258);
--color-tw10-sub: #55657c;
--color-tw10-sub: oklch(0.5 0.03 255);
--color-tw10-line: #eef1f5;
--color-tw10-line: oklch(0.95 0.005 255);
--color-tw10-brand: #e05252;
--color-tw10-brand: oklch(0.62 0.17 22);
--color-tw10-branddark: #c23e3e;
--color-tw10-branddark: oklch(0.55 0.17 22);
--color-tw10-glow: #ff6b6b80;
--color-tw10-glow: oklch(0.7 0.17 22 / 0.5);
--color-tw10-good: #047857;
--color-tw10-good: oklch(0.52 0.12 164);
--color-tw10-goodtint: #0596691f;
--color-tw10-goodtint: oklch(0.63 0.14 163 / 0.12);
--color-tw10-scrim: #1e293b80;
--color-tw10-scrim: oklch(0.3 0.03 258 / 0.5);
--font-tw10-sans: 'Outfit', system-ui, sans-serif;
}
.tw-10 {
width: 100%;
}
/* Placeholder product swatch — swap for a real <img>. */
.tw-10 [data-tw10-thumb] {
background-image: linear-gradient(135deg, oklch(0.93 0.04 350), oklch(0.95 0.06 90));
}
#tw-10-dlg::backdrop {
background: oklch(0.3 0.03 258 / 0);
backdrop-filter: blur(0px);
transition: background .25s ease, backdrop-filter .25s ease, overlay .25s allow-discrete, display .25s allow-discrete;
}
@starting-style {
#tw-10-dlg[open]::backdrop {
background: oklch(0.3 0.03 258 / 0);
backdrop-filter: blur(0px);
}
}
@media (prefers-reduced-motion: reduce) {
#tw-10-dlg::backdrop {
transition-duration: .001s;
}
}
```
## JavaScript
```js
(function () {
var root = document.querySelector('.tw-10');
var dlg = root.querySelector('#tw-10-dlg');
var card = dlg.firstElementChild;
root.querySelector('[data-tw10-open]').addEventListener('click', function () { dlg.showModal(); });
root.querySelectorAll('[data-tw10-close]').forEach(function (b) {
b.addEventListener('click', function () { dlg.close(); });
});
dlg.addEventListener('click', function (e) {
var r = card.getBoundingClientRect();
if (e.clientX < r.left || e.clientX > r.right || e.clientY < r.top || e.clientY > r.bottom) dlg.close();
});
})();
```How this works
The goo is an SVG filter, applied and then removed. While the card scales up it passes through feGaussianBlur + feColorMatrix, which merges its edges like liquid; a delayed filter transition drops to none at settle so type is never soft in the resting state.
The spring is honest about its overshoot. cubic-bezier(.34,1.56,.64,1) passes ~10% beyond target — delightful on a success moment, which is exactly the context that earns it.
The confirmation names the item. A bare "Added to cart" is useless to someone who added three things quickly; the product row, size, quantity and running subtotal are the content, and the animation is the garnish.
Make it yours
- Tune the blob with the filter's
stdDeviation— 4 is a hint, 14 is dramatic, 20+ smears the type. - Tighten or loosen the merge via the alpha column of the colour matrix.
- Add an auto-dismiss if your funnel wants one, with a pause-on-hover and a visible countdown.
- Skip the auto-dismiss entirely on high-value carts — a $500 confirmation should wait for the shopper.
Gotchas — read before shipping
- Remove the filter at settle. A permanently filtered card renders text through a threshold matrix, and small type turns to mush.
- The filter is decorative and unreadable by assistive tech — the
aria-liveline is what actually communicates the cart change. - Keep the card background solid. The alpha threshold produces harsh fringes over translucent or photographic backgrounds.
- An overshooting scale can briefly exceed a 320px viewport; test the small end or clamp the card width as here.
- Auto-dismiss without a pause control is a WCAG 2.2.1 problem, not a conversion tactic.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ · 117+ anim | 16.4+ · 17.5+ anim | 128+ · 129+ anim | 111+ · 117+ anim |
SVG filter chains are universal since 2022; the @starting-style spring is the newer gate and degrades to an instant show.