36 Tailwind CSS Modals
21 / 36

All 36 in 36 Tailwind CSS Modals

  1. 01Tailwind Login / Signup Modal with Native Dialog & Starting-Style
  2. 02Tailwind Password Reset Modal with In-Place Success State
  3. 03Tailwind Age Verification Gate with :has() and Zero JavaScript
  4. 04Tailwind Paywall Modal with Masked Article Fade
  5. 05Tailwind Product Quick-View Modal with has-checked Variant Chips
  6. 06Tailwind Shopping Cart Slide-Over Drawer with Dialog
  7. 07Tailwind Timed Newsletter Promo Popup with Frequency Cap
  8. 08Tailwind Size Guide Overlay with Zero-JS cm/inch Toggle
  9. 09Tailwind Zero-JS Lightbox Gallery with the target Variant
  10. 10Tailwind Video Trailer Modal with Aspect-Locked Iframe
  11. 11Tailwind Destructive Delete Modal with Type-to-Confirm Gate
  12. 12Tailwind Success / Error Result Modal with Drawn SVG Icon
  13. 13Tailwind GDPR Cookie Consent Banner with details Disclosure
  14. 14Tailwind Multi-Step Onboarding Modal with data-step Variants
  15. 15Tailwind Filter & Sort Drawer that Becomes a Bottom Sheet
  16. 16Tailwind Contact Support Modal with Live Validation
  17. 17Tailwind Tabbed Settings Modal with Zero-JS Radio Tabs
  18. 18Tailwind Command Palette Modal with ⌘K and Roving Selection
  19. 19Tailwind Confirm Delete Modal with Corner Slide-and-Spin Entrance
  20. 20Tailwind Login Modal with Staggered Blur-In Content Drop
  21. 21Tailwind Signup Modal with 3D Origami Fan Deploy Panels
  22. 22Tailwind Payment Checkout Modal with 3D Paper Fold Entrance
  23. 23Tailwind HIPAA Patient Consent Modal with Scroll-Gated Agreement
  24. 24Tailwind Age Verification Modal with Clip-Path Iris Aperture
  25. 25Tailwind GDPR Cookie Preferences Modal with Unfolding Category Rows
  26. 26Tailwind Video Lightbox Modal with Chromatic Glitch Materialize
  27. 27Tailwind Image Gallery Lightbox with Clip-Path Diamond Reveal
  28. 28Tailwind Add to Cart Confirmation with SVG Liquid Morph Entrance
  29. 29Tailwind Product Quick View Modal with Elastic Button Morph
  30. 30Tailwind Newsletter Signup Modal with Depth-Layer Parallax Entrance
  31. 31Tailwind Exit-Intent Discount Modal with Live Countdown and Coupon Copy
  32. 32Tailwind Restaurant Reservation Modal with Cascading Fan Panels
  33. 33Tailwind Order Success Modal with CSS-Only Confetti Burst
  34. 34Tailwind SaaS Onboarding Welcome Modal with Cascading Blur-In
  35. 35Tailwind Terms of Service Modal with Letter-Slide and Consent Gate
  36. 36Tailwind Password Reset Modal with Enumeration-Safe Confirmation
Tailwind + JSMIT licensed

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.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

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">&#10005;</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 &rarr;</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;
  }
}
(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.

1Load Tailwind
<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.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@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;
  }
}
3Fonts used by this demooptional — skip if you have your own type stack
<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 than transform: rotateX(…), matching what rotate-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.
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS 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">&#10005;</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 &rarr;</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-top to origin-bottom and flipping the sign in the keyframes.
  • Flatten or exaggerate the depth with perspective-[2400px] or perspective-[800px].
  • Drop the bounce by retuning --animate-tw03-fold's easing to cubic-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-hidden is 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-password here 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

ChromeSafariFirefoxEdge
111+ · 117+ anim16.4+ · 17.5+ anim128+ · 129+ anim111+ · 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.

Techniques used in this demo

Arbitraryneeds bracket syntax

HTML & JSno Tailwind involved

Search CodeFronts

Loading…