21 / 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 Signup Modal with 3D Origami Fan Deploy Panels
A registration card whose three horizontal panels — header, form, benefits strip — fan down like origami, each starting folded back on the X axis. The port uses v4's perspective-*, transform-3d and backface-hidden utilities for the 3D stage and one animate-tw03-fold theme animation with three [animation-delay:…] offsets.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tw-03 w-full min-h-svh grid place-items-center bg-tw03-bg font-tw03-sans px-6 py-10">
<button type="button" data-tw03-open class="inline-flex items-center justify-center min-h-11 px-6 rounded-[9px] border-0 text-[15px] font-semibold text-white bg-tw03-good shadow-[0_8px_20px_-6px_var(--color-tw03-glow)] cursor-pointer transition-[background-color,translate] duration-200 hover:bg-tw03-goodhover hover:-translate-y-px focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2 motion-reduce:hover:translate-y-0">Create account</button>
<dialog id="tw-03-dlg" aria-labelledby="tw-03-title" class="group m-auto w-[min(460px,94vw)] p-0 border-0 bg-transparent perspective-[1400px] opacity-0 transition-[opacity,overlay,display] duration-200 transition-discrete open:opacity-100 open:starting:opacity-0 open:backdrop:bg-tw03-scrim open:backdrop:backdrop-blur-[6px]">
<div class="relative rounded-[14px] overflow-hidden bg-tw03-card text-tw03-ink shadow-[0_40px_60px_-20px_#00000059] transform-3d">
<button type="button" data-tw03-close aria-label="Close dialog" class="absolute top-3.5 right-3.5 z-10 grid place-items-center size-9 rounded-lg border-0 bg-transparent text-tw03-sub text-base cursor-pointer transition-colors duration-200 hover:bg-tw03-well hover:text-tw03-ink focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2">✕</button>
<div class="px-8 pt-8 pb-5 opacity-0 rotate-x-[-90deg] origin-top backface-hidden group-open:animate-tw03-fold [animation-delay:150ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:rotate-x-0">
<h2 id="tw-03-title" class="mb-1.5 text-[1.55rem] font-extrabold tracking-[-0.02em] leading-tight">Create your free account</h2>
<p class="text-sm text-tw03-sub">Start your 14-day trial · No credit card required</p>
</div>
<div class="px-8 pb-5 opacity-0 rotate-x-[-90deg] origin-top backface-hidden group-open:animate-tw03-fold [animation-delay:350ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:rotate-x-0">
<form method="dialog" class="flex flex-col gap-3.5">
<label class="flex flex-col gap-1.5">
<span class="text-[13px] font-semibold text-tw03-sub">How should we call you?</span>
<input type="text" autofocus required autocomplete="given-name" placeholder="Alex" class="min-h-11 px-3.5 rounded-[9px] border border-tw03-line bg-tw03-well text-[15px] text-tw03-ink placeholder:text-tw03-sub transition-[border-color,background-color] duration-200 focus:outline-none focus:border-tw03-good focus:bg-white">
</label>
<label class="flex flex-col gap-1.5">
<span class="text-[13px] font-semibold text-tw03-sub">Email</span>
<input type="email" required autocomplete="email" placeholder="alex@company.com" class="min-h-11 px-3.5 rounded-[9px] border border-tw03-line bg-tw03-well text-[15px] text-tw03-ink placeholder:text-tw03-sub transition-[border-color,background-color] duration-200 focus:outline-none focus:border-tw03-good focus:bg-white">
</label>
<label class="flex flex-col gap-1.5">
<span class="text-[13px] font-semibold text-tw03-sub">Password</span>
<input type="password" required minlength="10" autocomplete="new-password" spellcheck="false" class="min-h-11 px-3.5 rounded-[9px] border border-tw03-line bg-tw03-well text-[15px] text-tw03-ink transition-[border-color,background-color] duration-200 focus:outline-none focus:border-tw03-good focus:bg-white">
<span aria-hidden="true" class="grid grid-cols-4 gap-1 mt-1.5">
<span class="h-1 rounded-full bg-tw03-good"></span>
<span class="h-1 rounded-full bg-tw03-good"></span>
<span class="h-1 rounded-full bg-tw03-good"></span>
<span class="h-1 rounded-full bg-tw03-line"></span>
</span>
<small class="text-xs tracking-wide text-tw03-sub">Good · 10+ characters · Not in breach databases</small>
</label>
<label class="flex items-start gap-2.5 py-1 text-[13.5px] leading-snug cursor-pointer">
<input type="checkbox" required class="shrink-0 mt-0.5 size-4 accent-tw03-good cursor-pointer focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2">
<span>I agree to the <a href="#" class="text-tw03-good underline">Terms of Service</a> and <a href="#" class="text-tw03-good underline">Privacy Policy</a></span>
</label>
<label class="flex items-start gap-2.5 py-1 text-[13.5px] leading-snug cursor-pointer">
<input type="checkbox" class="shrink-0 mt-0.5 size-4 accent-tw03-good cursor-pointer focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2">
<span>Send me product updates and CSS tips (optional)</span>
</label>
<button type="submit" class="min-h-11 mt-1.5 px-5 rounded-[9px] border-0 bg-tw03-good text-[15px] font-bold text-white shadow-[0_8px_20px_-6px_var(--color-tw03-glow)] cursor-pointer transition-[background-color,translate] duration-200 hover:bg-tw03-goodhover hover:-translate-y-px focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2 motion-reduce:hover:translate-y-0">Create free account →</button>
</form>
</div>
<div class="px-8 pt-4 pb-[22px] border-t border-tw03-line opacity-0 rotate-x-[-90deg] origin-top backface-hidden group-open:animate-tw03-fold [animation-delay:550ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:rotate-x-0">
<ul class="flex flex-wrap justify-center gap-4 list-none text-xs font-semibold tracking-wide text-tw03-sub">
<li>✓ 14-day trial</li>
<li>✓ No credit card</li>
<li>✓ Cancel anytime</li>
</ul>
</div>
</div>
</dialog>
</section><section class="tw-03 w-full min-h-svh grid place-items-center bg-tw03-bg font-tw03-sans px-6 py-10">
<button type="button" data-tw03-open class="inline-flex items-center justify-center min-h-11 px-6 rounded-[9px] border-0 text-[15px] font-semibold text-white bg-tw03-good shadow-[0_8px_20px_-6px_var(--color-tw03-glow)] cursor-pointer transition-[background-color,translate] duration-200 hover:bg-tw03-goodhover hover:-translate-y-px focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2 motion-reduce:hover:translate-y-0">Create account</button>
<dialog id="tw-03-dlg" aria-labelledby="tw-03-title" class="group m-auto w-[min(460px,94vw)] p-0 border-0 bg-transparent perspective-[1400px] opacity-0 transition-[opacity,overlay,display] duration-200 transition-discrete open:opacity-100 open:starting:opacity-0 open:backdrop:bg-tw03-scrim open:backdrop:backdrop-blur-[6px]">
<div class="relative rounded-[14px] overflow-hidden bg-tw03-card text-tw03-ink shadow-[0_40px_60px_-20px_#00000059] transform-3d">
<button type="button" data-tw03-close aria-label="Close dialog" class="absolute top-3.5 right-3.5 z-10 grid place-items-center size-9 rounded-lg border-0 bg-transparent text-tw03-sub text-base cursor-pointer transition-colors duration-200 hover:bg-tw03-well hover:text-tw03-ink focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2">✕</button>
<div class="px-8 pt-8 pb-5 opacity-0 rotate-x-[-90deg] origin-top backface-hidden group-open:animate-tw03-fold [animation-delay:150ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:rotate-x-0">
<h2 id="tw-03-title" class="mb-1.5 text-[1.55rem] font-extrabold tracking-[-0.02em] leading-tight">Create your free account</h2>
<p class="text-sm text-tw03-sub">Start your 14-day trial · No credit card required</p>
</div>
<div class="px-8 pb-5 opacity-0 rotate-x-[-90deg] origin-top backface-hidden group-open:animate-tw03-fold [animation-delay:350ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:rotate-x-0">
<form method="dialog" class="flex flex-col gap-3.5">
<label class="flex flex-col gap-1.5">
<span class="text-[13px] font-semibold text-tw03-sub">How should we call you?</span>
<input type="text" autofocus required autocomplete="given-name" placeholder="Alex" class="min-h-11 px-3.5 rounded-[9px] border border-tw03-line bg-tw03-well text-[15px] text-tw03-ink placeholder:text-tw03-sub transition-[border-color,background-color] duration-200 focus:outline-none focus:border-tw03-good focus:bg-white">
</label>
<label class="flex flex-col gap-1.5">
<span class="text-[13px] font-semibold text-tw03-sub">Email</span>
<input type="email" required autocomplete="email" placeholder="alex@company.com" class="min-h-11 px-3.5 rounded-[9px] border border-tw03-line bg-tw03-well text-[15px] text-tw03-ink placeholder:text-tw03-sub transition-[border-color,background-color] duration-200 focus:outline-none focus:border-tw03-good focus:bg-white">
</label>
<label class="flex flex-col gap-1.5">
<span class="text-[13px] font-semibold text-tw03-sub">Password</span>
<input type="password" required minlength="10" autocomplete="new-password" spellcheck="false" class="min-h-11 px-3.5 rounded-[9px] border border-tw03-line bg-tw03-well text-[15px] text-tw03-ink transition-[border-color,background-color] duration-200 focus:outline-none focus:border-tw03-good focus:bg-white">
<span aria-hidden="true" class="grid grid-cols-4 gap-1 mt-1.5">
<span class="h-1 rounded-full bg-tw03-good"></span>
<span class="h-1 rounded-full bg-tw03-good"></span>
<span class="h-1 rounded-full bg-tw03-good"></span>
<span class="h-1 rounded-full bg-tw03-line"></span>
</span>
<small class="text-xs tracking-wide text-tw03-sub">Good · 10+ characters · Not in breach databases</small>
</label>
<label class="flex items-start gap-2.5 py-1 text-[13.5px] leading-snug cursor-pointer">
<input type="checkbox" required class="shrink-0 mt-0.5 size-4 accent-tw03-good cursor-pointer focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2">
<span>I agree to the <a href="#" class="text-tw03-good underline">Terms of Service</a> and <a href="#" class="text-tw03-good underline">Privacy Policy</a></span>
</label>
<label class="flex items-start gap-2.5 py-1 text-[13.5px] leading-snug cursor-pointer">
<input type="checkbox" class="shrink-0 mt-0.5 size-4 accent-tw03-good cursor-pointer focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2">
<span>Send me product updates and CSS tips (optional)</span>
</label>
<button type="submit" class="min-h-11 mt-1.5 px-5 rounded-[9px] border-0 bg-tw03-good text-[15px] font-bold text-white shadow-[0_8px_20px_-6px_var(--color-tw03-glow)] cursor-pointer transition-[background-color,translate] duration-200 hover:bg-tw03-goodhover hover:-translate-y-px focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2 motion-reduce:hover:translate-y-0">Create free account →</button>
</form>
</div>
<div class="px-8 pt-4 pb-[22px] border-t border-tw03-line opacity-0 rotate-x-[-90deg] origin-top backface-hidden group-open:animate-tw03-fold [animation-delay:550ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:rotate-x-0">
<ul class="flex flex-wrap justify-center gap-4 list-none text-xs font-semibold tracking-wide text-tw03-sub">
<li>✓ 14-day trial</li>
<li>✓ No credit card</li>
<li>✓ Cancel anytime</li>
</ul>
</div>
</div>
</dialog>
</section>@import "tailwindcss";
@theme {
--color-tw03-bg: #f7f8fa;
--color-tw03-bg: oklch(0.98 0.003 255);
--color-tw03-card: #ffffff;
--color-tw03-card: oklch(1 0 0);
--color-tw03-well: #f7f8fa;
--color-tw03-well: oklch(0.97 0.004 255);
--color-tw03-ink: #0f0f14;
--color-tw03-ink: oklch(0.18 0.01 285);
--color-tw03-sub: #6b7280;
--color-tw03-sub: oklch(0.56 0.02 265);
--color-tw03-line: #e5e7eb;
--color-tw03-line: oklch(0.92 0.004 265);
--color-tw03-good: #10b981;
--color-tw03-good: oklch(0.7 0.15 163);
--color-tw03-goodhover: #059669;
--color-tw03-goodhover: oklch(0.63 0.14 163);
--color-tw03-glow: #10b98180;
--color-tw03-glow: oklch(0.7 0.15 163 / 0.5);
--color-tw03-scrim: #0f172a99;
--color-tw03-scrim: oklch(0.23 0.04 265 / 0.6);
--font-tw03-sans: 'Outfit', system-ui, sans-serif;
--animate-tw03-fold: tw03-fold .5s cubic-bezier(.34, 1.12, .5, 1) forwards;
}
.tw-03 {
width: 100%;
}
/* The fold's keyframes. rotate is the individual property v4's rotate-x-* utilities
also write, so the resting utility and the animation agree. */
@keyframes tw03-fold {
from {
opacity: 0;
rotate: x -90deg;
}
50% {
opacity: 1;
}
to {
opacity: 1;
rotate: x 0deg;
}
}
#tw-03-dlg::backdrop {
background: oklch(0.23 0.04 265 / 0);
backdrop-filter: blur(0px);
transition: background .22s ease, backdrop-filter .22s ease, overlay .22s allow-discrete, display .22s allow-discrete;
}
@starting-style {
#tw-03-dlg[open]::backdrop {
background: oklch(0.23 0.04 265 / 0);
backdrop-filter: blur(0px);
}
}
@media (prefers-reduced-motion: reduce) {
#tw-03-dlg::backdrop {
transition-duration: .001s;
}
}@import "tailwindcss";
@theme {
--color-tw03-bg: #f7f8fa;
--color-tw03-bg: oklch(0.98 0.003 255);
--color-tw03-card: #ffffff;
--color-tw03-card: oklch(1 0 0);
--color-tw03-well: #f7f8fa;
--color-tw03-well: oklch(0.97 0.004 255);
--color-tw03-ink: #0f0f14;
--color-tw03-ink: oklch(0.18 0.01 285);
--color-tw03-sub: #6b7280;
--color-tw03-sub: oklch(0.56 0.02 265);
--color-tw03-line: #e5e7eb;
--color-tw03-line: oklch(0.92 0.004 265);
--color-tw03-good: #10b981;
--color-tw03-good: oklch(0.7 0.15 163);
--color-tw03-goodhover: #059669;
--color-tw03-goodhover: oklch(0.63 0.14 163);
--color-tw03-glow: #10b98180;
--color-tw03-glow: oklch(0.7 0.15 163 / 0.5);
--color-tw03-scrim: #0f172a99;
--color-tw03-scrim: oklch(0.23 0.04 265 / 0.6);
--font-tw03-sans: 'Outfit', system-ui, sans-serif;
--animate-tw03-fold: tw03-fold .5s cubic-bezier(.34, 1.12, .5, 1) forwards;
}
.tw-03 {
width: 100%;
}
/* The fold's keyframes. rotate is the individual property v4's rotate-x-* utilities
also write, so the resting utility and the animation agree. */
@keyframes tw03-fold {
from {
opacity: 0;
rotate: x -90deg;
}
50% {
opacity: 1;
}
to {
opacity: 1;
rotate: x 0deg;
}
}
#tw-03-dlg::backdrop {
background: oklch(0.23 0.04 265 / 0);
backdrop-filter: blur(0px);
transition: background .22s ease, backdrop-filter .22s ease, overlay .22s allow-discrete, display .22s allow-discrete;
}
@starting-style {
#tw-03-dlg[open]::backdrop {
background: oklch(0.23 0.04 265 / 0);
backdrop-filter: blur(0px);
}
}
@media (prefers-reduced-motion: reduce) {
#tw-03-dlg::backdrop {
transition-duration: .001s;
}
}(function () {
var root = document.querySelector('.tw-03');
var dlg = root.querySelector('#tw-03-dlg');
var card = dlg.firstElementChild;
root.querySelector('[data-tw03-open]').addEventListener('click', function () { dlg.showModal(); });
root.querySelector('[data-tw03-close]').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-03');
var dlg = root.querySelector('#tw-03-dlg');
var card = dlg.firstElementChild;
root.querySelector('[data-tw03-open]').addEventListener('click', function () { dlg.showModal(); });
root.querySelector('[data-tw03-close]').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-tw03-bg: #f7f8fa;
--color-tw03-bg: oklch(0.98 0.003 255);
--color-tw03-card: #ffffff;
--color-tw03-card: oklch(1 0 0);
--color-tw03-well: #f7f8fa;
--color-tw03-well: oklch(0.97 0.004 255);
--color-tw03-ink: #0f0f14;
--color-tw03-ink: oklch(0.18 0.01 285);
--color-tw03-sub: #6b7280;
--color-tw03-sub: oklch(0.56 0.02 265);
--color-tw03-line: #e5e7eb;
--color-tw03-line: oklch(0.92 0.004 265);
--color-tw03-good: #10b981;
--color-tw03-good: oklch(0.7 0.15 163);
--color-tw03-goodhover: #059669;
--color-tw03-goodhover: oklch(0.63 0.14 163);
--color-tw03-glow: #10b98180;
--color-tw03-glow: oklch(0.7 0.15 163 / 0.5);
--color-tw03-scrim: #0f172a99;
--color-tw03-scrim: oklch(0.23 0.04 265 / 0.6);
--font-tw03-sans: 'Outfit', system-ui, sans-serif;
--animate-tw03-fold: tw03-fold .5s cubic-bezier(.34, 1.12, .5, 1) forwards;
}
.tw-03 {
width: 100%;
}
/* The fold's keyframes. rotate is the individual property v4's rotate-x-* utilities
also write, so the resting utility and the animation agree. */
@keyframes tw03-fold {
from {
opacity: 0;
rotate: x -90deg;
}
50% {
opacity: 1;
}
to {
opacity: 1;
rotate: x 0deg;
}
}
#tw-03-dlg::backdrop {
background: oklch(0.23 0.04 265 / 0);
backdrop-filter: blur(0px);
transition: background .22s ease, backdrop-filter .22s ease, overlay .22s allow-discrete, display .22s allow-discrete;
}
@starting-style {
#tw-03-dlg[open]::backdrop {
background: oklch(0.23 0.04 265 / 0);
backdrop-filter: blur(0px);
}
}
@media (prefers-reduced-motion: reduce) {
#tw-03-dlg::backdrop {
transition-duration: .001s;
}
}@import "tailwindcss";
@theme {
--color-tw03-bg: #f7f8fa;
--color-tw03-bg: oklch(0.98 0.003 255);
--color-tw03-card: #ffffff;
--color-tw03-card: oklch(1 0 0);
--color-tw03-well: #f7f8fa;
--color-tw03-well: oklch(0.97 0.004 255);
--color-tw03-ink: #0f0f14;
--color-tw03-ink: oklch(0.18 0.01 285);
--color-tw03-sub: #6b7280;
--color-tw03-sub: oklch(0.56 0.02 265);
--color-tw03-line: #e5e7eb;
--color-tw03-line: oklch(0.92 0.004 265);
--color-tw03-good: #10b981;
--color-tw03-good: oklch(0.7 0.15 163);
--color-tw03-goodhover: #059669;
--color-tw03-goodhover: oklch(0.63 0.14 163);
--color-tw03-glow: #10b98180;
--color-tw03-glow: oklch(0.7 0.15 163 / 0.5);
--color-tw03-scrim: #0f172a99;
--color-tw03-scrim: oklch(0.23 0.04 265 / 0.6);
--font-tw03-sans: 'Outfit', system-ui, sans-serif;
--animate-tw03-fold: tw03-fold .5s cubic-bezier(.34, 1.12, .5, 1) forwards;
}
.tw-03 {
width: 100%;
}
/* The fold's keyframes. rotate is the individual property v4's rotate-x-* utilities
also write, so the resting utility and the animation agree. */
@keyframes tw03-fold {
from {
opacity: 0;
rotate: x -90deg;
}
50% {
opacity: 1;
}
to {
opacity: 1;
rotate: x 0deg;
}
}
#tw-03-dlg::backdrop {
background: oklch(0.23 0.04 265 / 0);
backdrop-filter: blur(0px);
transition: background .22s ease, backdrop-filter .22s ease, overlay .22s allow-discrete, display .22s allow-discrete;
}
@starting-style {
#tw-03-dlg[open]::backdrop {
background: oklch(0.23 0.04 265 / 0);
backdrop-filter: blur(0px);
}
}
@media (prefers-reduced-motion: reduce) {
#tw-03-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.
- v4 ships the 3D stage as utilities —
perspective-[1400px],transform-3d,backface-hidden,rotate-x-*— so the only CSS left for the fold is the keyframe block itself. - The keyframes write
rotate: x …rather thantransform: rotateX(…), matching whatrotate-x-*emits; mixing the two forms makes the resting state and the animation fight. - The
<dialog>is transparent and holds the perspective; the visible card is its child. Perspective on the same element that transforms has no effect, which is the classic 3D-in-a-modal mistake. - Behaviour change from the CSS original: native
<dialog>+showModal()replaces the class-toggled div, so focus trapping, Esc and scroll lock come from the browser. - UI change: inputs and buttons are 44px, the consent rows have real padding so their hit areas do not overlap, and the strength meter is a 4-column grid instead of flex children with fixed widths.
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 Signup Modal with 3D Origami Fan Deploy Panels
Source: https://codefronts.com/components/tailwind-css-modals/tailwind-signup-modal-origami-fan-deploy-3d-panels/
A registration card whose three horizontal panels — header, form, benefits strip — fan down like origami, each starting folded back on the X axis. The port uses v4's perspective-*, transform-3d and backface-hidden utilities for the 3D stage and one animate-tw03-fold theme animation with three [animation-delay:…] offsets.
## HTML
```html
<section class="tw-03 w-full min-h-svh grid place-items-center bg-tw03-bg font-tw03-sans px-6 py-10">
<button type="button" data-tw03-open class="inline-flex items-center justify-center min-h-11 px-6 rounded-[9px] border-0 text-[15px] font-semibold text-white bg-tw03-good shadow-[0_8px_20px_-6px_var(--color-tw03-glow)] cursor-pointer transition-[background-color,translate] duration-200 hover:bg-tw03-goodhover hover:-translate-y-px focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2 motion-reduce:hover:translate-y-0">Create account</button>
<dialog id="tw-03-dlg" aria-labelledby="tw-03-title" class="group m-auto w-[min(460px,94vw)] p-0 border-0 bg-transparent perspective-[1400px] opacity-0 transition-[opacity,overlay,display] duration-200 transition-discrete open:opacity-100 open:starting:opacity-0 open:backdrop:bg-tw03-scrim open:backdrop:backdrop-blur-[6px]">
<div class="relative rounded-[14px] overflow-hidden bg-tw03-card text-tw03-ink shadow-[0_40px_60px_-20px_#00000059] transform-3d">
<button type="button" data-tw03-close aria-label="Close dialog" class="absolute top-3.5 right-3.5 z-10 grid place-items-center size-9 rounded-lg border-0 bg-transparent text-tw03-sub text-base cursor-pointer transition-colors duration-200 hover:bg-tw03-well hover:text-tw03-ink focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2">✕</button>
<div class="px-8 pt-8 pb-5 opacity-0 rotate-x-[-90deg] origin-top backface-hidden group-open:animate-tw03-fold [animation-delay:150ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:rotate-x-0">
<h2 id="tw-03-title" class="mb-1.5 text-[1.55rem] font-extrabold tracking-[-0.02em] leading-tight">Create your free account</h2>
<p class="text-sm text-tw03-sub">Start your 14-day trial · No credit card required</p>
</div>
<div class="px-8 pb-5 opacity-0 rotate-x-[-90deg] origin-top backface-hidden group-open:animate-tw03-fold [animation-delay:350ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:rotate-x-0">
<form method="dialog" class="flex flex-col gap-3.5">
<label class="flex flex-col gap-1.5">
<span class="text-[13px] font-semibold text-tw03-sub">How should we call you?</span>
<input type="text" autofocus required autocomplete="given-name" placeholder="Alex" class="min-h-11 px-3.5 rounded-[9px] border border-tw03-line bg-tw03-well text-[15px] text-tw03-ink placeholder:text-tw03-sub transition-[border-color,background-color] duration-200 focus:outline-none focus:border-tw03-good focus:bg-white">
</label>
<label class="flex flex-col gap-1.5">
<span class="text-[13px] font-semibold text-tw03-sub">Email</span>
<input type="email" required autocomplete="email" placeholder="alex@company.com" class="min-h-11 px-3.5 rounded-[9px] border border-tw03-line bg-tw03-well text-[15px] text-tw03-ink placeholder:text-tw03-sub transition-[border-color,background-color] duration-200 focus:outline-none focus:border-tw03-good focus:bg-white">
</label>
<label class="flex flex-col gap-1.5">
<span class="text-[13px] font-semibold text-tw03-sub">Password</span>
<input type="password" required minlength="10" autocomplete="new-password" spellcheck="false" class="min-h-11 px-3.5 rounded-[9px] border border-tw03-line bg-tw03-well text-[15px] text-tw03-ink transition-[border-color,background-color] duration-200 focus:outline-none focus:border-tw03-good focus:bg-white">
<span aria-hidden="true" class="grid grid-cols-4 gap-1 mt-1.5">
<span class="h-1 rounded-full bg-tw03-good"></span>
<span class="h-1 rounded-full bg-tw03-good"></span>
<span class="h-1 rounded-full bg-tw03-good"></span>
<span class="h-1 rounded-full bg-tw03-line"></span>
</span>
<small class="text-xs tracking-wide text-tw03-sub">Good · 10+ characters · Not in breach databases</small>
</label>
<label class="flex items-start gap-2.5 py-1 text-[13.5px] leading-snug cursor-pointer">
<input type="checkbox" required class="shrink-0 mt-0.5 size-4 accent-tw03-good cursor-pointer focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2">
<span>I agree to the <a href="#" class="text-tw03-good underline">Terms of Service</a> and <a href="#" class="text-tw03-good underline">Privacy Policy</a></span>
</label>
<label class="flex items-start gap-2.5 py-1 text-[13.5px] leading-snug cursor-pointer">
<input type="checkbox" class="shrink-0 mt-0.5 size-4 accent-tw03-good cursor-pointer focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2">
<span>Send me product updates and CSS tips (optional)</span>
</label>
<button type="submit" class="min-h-11 mt-1.5 px-5 rounded-[9px] border-0 bg-tw03-good text-[15px] font-bold text-white shadow-[0_8px_20px_-6px_var(--color-tw03-glow)] cursor-pointer transition-[background-color,translate] duration-200 hover:bg-tw03-goodhover hover:-translate-y-px focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2 motion-reduce:hover:translate-y-0">Create free account →</button>
</form>
</div>
<div class="px-8 pt-4 pb-[22px] border-t border-tw03-line opacity-0 rotate-x-[-90deg] origin-top backface-hidden group-open:animate-tw03-fold [animation-delay:550ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:rotate-x-0">
<ul class="flex flex-wrap justify-center gap-4 list-none text-xs font-semibold tracking-wide text-tw03-sub">
<li>✓ 14-day trial</li>
<li>✓ No credit card</li>
<li>✓ Cancel anytime</li>
</ul>
</div>
</div>
</dialog>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tw03-bg: #f7f8fa;
--color-tw03-bg: oklch(0.98 0.003 255);
--color-tw03-card: #ffffff;
--color-tw03-card: oklch(1 0 0);
--color-tw03-well: #f7f8fa;
--color-tw03-well: oklch(0.97 0.004 255);
--color-tw03-ink: #0f0f14;
--color-tw03-ink: oklch(0.18 0.01 285);
--color-tw03-sub: #6b7280;
--color-tw03-sub: oklch(0.56 0.02 265);
--color-tw03-line: #e5e7eb;
--color-tw03-line: oklch(0.92 0.004 265);
--color-tw03-good: #10b981;
--color-tw03-good: oklch(0.7 0.15 163);
--color-tw03-goodhover: #059669;
--color-tw03-goodhover: oklch(0.63 0.14 163);
--color-tw03-glow: #10b98180;
--color-tw03-glow: oklch(0.7 0.15 163 / 0.5);
--color-tw03-scrim: #0f172a99;
--color-tw03-scrim: oklch(0.23 0.04 265 / 0.6);
--font-tw03-sans: 'Outfit', system-ui, sans-serif;
--animate-tw03-fold: tw03-fold .5s cubic-bezier(.34, 1.12, .5, 1) forwards;
}
.tw-03 {
width: 100%;
}
/* The fold's keyframes. rotate is the individual property v4's rotate-x-* utilities
also write, so the resting utility and the animation agree. */
@keyframes tw03-fold {
from {
opacity: 0;
rotate: x -90deg;
}
50% {
opacity: 1;
}
to {
opacity: 1;
rotate: x 0deg;
}
}
#tw-03-dlg::backdrop {
background: oklch(0.23 0.04 265 / 0);
backdrop-filter: blur(0px);
transition: background .22s ease, backdrop-filter .22s ease, overlay .22s allow-discrete, display .22s allow-discrete;
}
@starting-style {
#tw-03-dlg[open]::backdrop {
background: oklch(0.23 0.04 265 / 0);
backdrop-filter: blur(0px);
}
}
@media (prefers-reduced-motion: reduce) {
#tw-03-dlg::backdrop {
transition-duration: .001s;
}
}
```
## JavaScript
```js
(function () {
var root = document.querySelector('.tw-03');
var dlg = root.querySelector('#tw-03-dlg');
var card = dlg.firstElementChild;
root.querySelector('[data-tw03-open]').addEventListener('click', function () { dlg.showModal(); });
root.querySelector('[data-tw03-close]').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 Signup Modal with 3D Origami Fan Deploy Panels
Source: https://codefronts.com/components/tailwind-css-modals/tailwind-signup-modal-origami-fan-deploy-3d-panels/
A registration card whose three horizontal panels — header, form, benefits strip — fan down like origami, each starting folded back on the X axis. The port uses v4's perspective-*, transform-3d and backface-hidden utilities for the 3D stage and one animate-tw03-fold theme animation with three [animation-delay:…] offsets.
## HTML
```html
<section class="tw-03 w-full min-h-svh grid place-items-center bg-tw03-bg font-tw03-sans px-6 py-10">
<button type="button" data-tw03-open class="inline-flex items-center justify-center min-h-11 px-6 rounded-[9px] border-0 text-[15px] font-semibold text-white bg-tw03-good shadow-[0_8px_20px_-6px_var(--color-tw03-glow)] cursor-pointer transition-[background-color,translate] duration-200 hover:bg-tw03-goodhover hover:-translate-y-px focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2 motion-reduce:hover:translate-y-0">Create account</button>
<dialog id="tw-03-dlg" aria-labelledby="tw-03-title" class="group m-auto w-[min(460px,94vw)] p-0 border-0 bg-transparent perspective-[1400px] opacity-0 transition-[opacity,overlay,display] duration-200 transition-discrete open:opacity-100 open:starting:opacity-0 open:backdrop:bg-tw03-scrim open:backdrop:backdrop-blur-[6px]">
<div class="relative rounded-[14px] overflow-hidden bg-tw03-card text-tw03-ink shadow-[0_40px_60px_-20px_#00000059] transform-3d">
<button type="button" data-tw03-close aria-label="Close dialog" class="absolute top-3.5 right-3.5 z-10 grid place-items-center size-9 rounded-lg border-0 bg-transparent text-tw03-sub text-base cursor-pointer transition-colors duration-200 hover:bg-tw03-well hover:text-tw03-ink focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2">✕</button>
<div class="px-8 pt-8 pb-5 opacity-0 rotate-x-[-90deg] origin-top backface-hidden group-open:animate-tw03-fold [animation-delay:150ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:rotate-x-0">
<h2 id="tw-03-title" class="mb-1.5 text-[1.55rem] font-extrabold tracking-[-0.02em] leading-tight">Create your free account</h2>
<p class="text-sm text-tw03-sub">Start your 14-day trial · No credit card required</p>
</div>
<div class="px-8 pb-5 opacity-0 rotate-x-[-90deg] origin-top backface-hidden group-open:animate-tw03-fold [animation-delay:350ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:rotate-x-0">
<form method="dialog" class="flex flex-col gap-3.5">
<label class="flex flex-col gap-1.5">
<span class="text-[13px] font-semibold text-tw03-sub">How should we call you?</span>
<input type="text" autofocus required autocomplete="given-name" placeholder="Alex" class="min-h-11 px-3.5 rounded-[9px] border border-tw03-line bg-tw03-well text-[15px] text-tw03-ink placeholder:text-tw03-sub transition-[border-color,background-color] duration-200 focus:outline-none focus:border-tw03-good focus:bg-white">
</label>
<label class="flex flex-col gap-1.5">
<span class="text-[13px] font-semibold text-tw03-sub">Email</span>
<input type="email" required autocomplete="email" placeholder="alex@company.com" class="min-h-11 px-3.5 rounded-[9px] border border-tw03-line bg-tw03-well text-[15px] text-tw03-ink placeholder:text-tw03-sub transition-[border-color,background-color] duration-200 focus:outline-none focus:border-tw03-good focus:bg-white">
</label>
<label class="flex flex-col gap-1.5">
<span class="text-[13px] font-semibold text-tw03-sub">Password</span>
<input type="password" required minlength="10" autocomplete="new-password" spellcheck="false" class="min-h-11 px-3.5 rounded-[9px] border border-tw03-line bg-tw03-well text-[15px] text-tw03-ink transition-[border-color,background-color] duration-200 focus:outline-none focus:border-tw03-good focus:bg-white">
<span aria-hidden="true" class="grid grid-cols-4 gap-1 mt-1.5">
<span class="h-1 rounded-full bg-tw03-good"></span>
<span class="h-1 rounded-full bg-tw03-good"></span>
<span class="h-1 rounded-full bg-tw03-good"></span>
<span class="h-1 rounded-full bg-tw03-line"></span>
</span>
<small class="text-xs tracking-wide text-tw03-sub">Good · 10+ characters · Not in breach databases</small>
</label>
<label class="flex items-start gap-2.5 py-1 text-[13.5px] leading-snug cursor-pointer">
<input type="checkbox" required class="shrink-0 mt-0.5 size-4 accent-tw03-good cursor-pointer focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2">
<span>I agree to the <a href="#" class="text-tw03-good underline">Terms of Service</a> and <a href="#" class="text-tw03-good underline">Privacy Policy</a></span>
</label>
<label class="flex items-start gap-2.5 py-1 text-[13.5px] leading-snug cursor-pointer">
<input type="checkbox" class="shrink-0 mt-0.5 size-4 accent-tw03-good cursor-pointer focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2">
<span>Send me product updates and CSS tips (optional)</span>
</label>
<button type="submit" class="min-h-11 mt-1.5 px-5 rounded-[9px] border-0 bg-tw03-good text-[15px] font-bold text-white shadow-[0_8px_20px_-6px_var(--color-tw03-glow)] cursor-pointer transition-[background-color,translate] duration-200 hover:bg-tw03-goodhover hover:-translate-y-px focus-visible:outline-2 focus-visible:outline-tw03-good focus-visible:outline-offset-2 motion-reduce:hover:translate-y-0">Create free account →</button>
</form>
</div>
<div class="px-8 pt-4 pb-[22px] border-t border-tw03-line opacity-0 rotate-x-[-90deg] origin-top backface-hidden group-open:animate-tw03-fold [animation-delay:550ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:rotate-x-0">
<ul class="flex flex-wrap justify-center gap-4 list-none text-xs font-semibold tracking-wide text-tw03-sub">
<li>✓ 14-day trial</li>
<li>✓ No credit card</li>
<li>✓ Cancel anytime</li>
</ul>
</div>
</div>
</dialog>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tw03-bg: #f7f8fa;
--color-tw03-bg: oklch(0.98 0.003 255);
--color-tw03-card: #ffffff;
--color-tw03-card: oklch(1 0 0);
--color-tw03-well: #f7f8fa;
--color-tw03-well: oklch(0.97 0.004 255);
--color-tw03-ink: #0f0f14;
--color-tw03-ink: oklch(0.18 0.01 285);
--color-tw03-sub: #6b7280;
--color-tw03-sub: oklch(0.56 0.02 265);
--color-tw03-line: #e5e7eb;
--color-tw03-line: oklch(0.92 0.004 265);
--color-tw03-good: #10b981;
--color-tw03-good: oklch(0.7 0.15 163);
--color-tw03-goodhover: #059669;
--color-tw03-goodhover: oklch(0.63 0.14 163);
--color-tw03-glow: #10b98180;
--color-tw03-glow: oklch(0.7 0.15 163 / 0.5);
--color-tw03-scrim: #0f172a99;
--color-tw03-scrim: oklch(0.23 0.04 265 / 0.6);
--font-tw03-sans: 'Outfit', system-ui, sans-serif;
--animate-tw03-fold: tw03-fold .5s cubic-bezier(.34, 1.12, .5, 1) forwards;
}
.tw-03 {
width: 100%;
}
/* The fold's keyframes. rotate is the individual property v4's rotate-x-* utilities
also write, so the resting utility and the animation agree. */
@keyframes tw03-fold {
from {
opacity: 0;
rotate: x -90deg;
}
50% {
opacity: 1;
}
to {
opacity: 1;
rotate: x 0deg;
}
}
#tw-03-dlg::backdrop {
background: oklch(0.23 0.04 265 / 0);
backdrop-filter: blur(0px);
transition: background .22s ease, backdrop-filter .22s ease, overlay .22s allow-discrete, display .22s allow-discrete;
}
@starting-style {
#tw-03-dlg[open]::backdrop {
background: oklch(0.23 0.04 265 / 0);
backdrop-filter: blur(0px);
}
}
@media (prefers-reduced-motion: reduce) {
#tw-03-dlg::backdrop {
transition-duration: .001s;
}
}
```
## JavaScript
```js
(function () {
var root = document.querySelector('.tw-03');
var dlg = root.querySelector('#tw-03-dlg');
var card = dlg.firstElementChild;
root.querySelector('[data-tw03-open]').addEventListener('click', function () { dlg.showModal(); });
root.querySelector('[data-tw03-close]').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 fold needs a stage. A wrapper carries perspective-[1400px]; the card carries transform-3d so its children fold in the same 3D space. Each panel rests at rotate-x-[-90deg] with origin-top, which is "folded up and away from the reader".
Three panels, three delays. group-open:animate-tw03-fold starts all three; the [animation-delay:…] utilities space them 200ms apart so they deploy in reading order.
Consent is two checkboxes, deliberately. Terms and marketing are separate controls, both unchecked — GDPR Article 7 requires consent to be distinguishable, so bundling them is not a layout shortcut but an unenforceable consent.
Make it yours
- Fan upward by moving
origin-toptoorigin-bottomand flipping the sign in the keyframes. - Flatten or exaggerate the depth with
perspective-[2400px]orperspective-[800px]. - Drop the bounce by retuning
--animate-tw03-fold's easing tocubic-bezier(.22,1,.36,1). - Add a workspace-name field for B2B signup; it inherits the panel's fold automatically.
Gotchas — read before shipping
backface-hiddenis not cosmetic here: without it Safari and Firefox can flash the panel's reverse face as it passes 90°.- The strength meter is presentational (
aria-hidden) and mirrored by real text — a colour-only strength signal is invisible to screen readers and to most colour-blind users. - Never pre-check the terms box, and never bundle marketing consent into it. Both are enforcement risks, not style choices.
autocomplete="new-password"tells managers to generate rather than autofill;current-passwordhere makes them offer the wrong credential.- Focus starts on the name field. Focusing the terms checkbox means a reflexive Space agrees to the terms.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ · 117+ anim | 16.4+ · 17.5+ anim | 128+ · 129+ anim | 111+ · 117+ anim |
CSS 3D transforms are Baseline 2015; only the dialog's @starting-style fade sits on the newer gate and degrades to an instant show.