36 Tailwind CSS Modals
20 / 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 Login Modal with Staggered Blur-In Content Drop

A SaaS sign-in whose shell appears crisp and whose contents develop like a photograph — brand, title, social row, divider, fields and submit each clearing an 8px blur on a staggered offset. The port turns the original's eleven nth-child delay rules into one animate-tw02-drop theme utility plus an [animation-delay:…] on each child.

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-02 w-full min-h-svh grid place-items-center bg-tw02-bg font-tw02-sans px-6 py-10">
  <button type="button" data-tw02-open class="inline-flex items-center justify-center min-h-11 px-6 rounded-[9px] border-0 text-[15px] font-semibold text-white bg-tw02-brand shadow-[0_8px_20px_-6px_var(--color-tw02-glow)] cursor-pointer transition-[translate,box-shadow] duration-200 hover:-translate-y-px hover:shadow-[0_12px_26px_-6px_var(--color-tw02-glow)] focus-visible:outline-2 focus-visible:outline-tw02-brandlt focus-visible:outline-offset-2 motion-reduce:hover:translate-y-0">Sign in</button>

  <dialog id="tw-02-dlg" aria-labelledby="tw-02-title" class="group m-auto w-[min(420px,94vw)] p-0 border border-tw02-line rounded-2xl text-tw02-ink bg-tw02-card shadow-[0_40px_60px_-20px_#000000a0] opacity-0 transition-[opacity,overlay,display] duration-200 transition-discrete open:opacity-100 open:starting:opacity-0 open:backdrop:bg-tw02-scrim open:backdrop:backdrop-blur-[8px]">
    <div class="relative px-8 pt-9 pb-7">
      <button type="button" data-tw02-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-tw02-sub text-base cursor-pointer transition-colors duration-200 hover:bg-tw02-hover hover:text-tw02-ink focus-visible:outline-2 focus-visible:outline-tw02-brandlt focus-visible:outline-offset-2">&#10005;</button>

      <div class="inline-flex items-center gap-2 mb-6 text-sm font-bold tracking-wide text-tw02-brandlt opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:150ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none">
        <span aria-hidden="true" class="text-sm leading-none">&#9670;</span><span>CodeFronts</span>
      </div>

      <h2 id="tw-02-title" class="mb-1.5 text-[1.6rem] font-extrabold tracking-[-0.02em] leading-tight opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:220ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none">Welcome back</h2>
      <p class="mb-6 text-[15px] text-tw02-sub opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:280ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none">Sign in to your workspace</p>

      <div class="flex flex-col gap-2 mb-[18px]">
        <button type="button" class="inline-flex items-center justify-center gap-2.5 min-h-11 px-3.5 rounded-[9px] border border-tw02-line bg-tw02-hover text-[15px] font-medium text-tw02-ink cursor-pointer transition-colors duration-200 hover:border-tw02-linehover hover:bg-tw02-hover2 focus-visible:outline-2 focus-visible:outline-tw02-brandlt focus-visible:outline-offset-2 opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:350ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none"><span aria-hidden="true" class="grid place-items-center size-5 rounded bg-white text-[0.72rem] font-extrabold text-tw02-bg">G</span>Continue with Google</button>
        <button type="button" class="inline-flex items-center justify-center gap-2.5 min-h-11 px-3.5 rounded-[9px] border border-tw02-line bg-tw02-hover text-[15px] font-medium text-tw02-ink cursor-pointer transition-colors duration-200 hover:border-tw02-linehover hover:bg-tw02-hover2 focus-visible:outline-2 focus-visible:outline-tw02-brandlt focus-visible:outline-offset-2 opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:400ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none"><span aria-hidden="true" class="grid place-items-center size-5 rounded bg-tw02-gh text-[0.85rem] font-extrabold text-white">&#9673;</span>Continue with GitHub</button>
        <button type="button" class="inline-flex items-center justify-center gap-2.5 min-h-11 px-3.5 rounded-[9px] border border-tw02-line bg-tw02-hover text-[15px] font-medium text-tw02-ink cursor-pointer transition-colors duration-200 hover:border-tw02-linehover hover:bg-tw02-hover2 focus-visible:outline-2 focus-visible:outline-tw02-brandlt focus-visible:outline-offset-2 opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:450ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none"><span aria-hidden="true" class="grid place-items-center size-5 rounded bg-tw02-brand text-[0.72rem] font-extrabold text-white">&#9889;</span>SSO / SAML</button>
      </div>

      <div class="flex items-center gap-3 my-4 text-xs tracking-wide text-tw02-sub opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:550ms] before:content-[''] before:flex-1 before:h-px before:bg-tw02-line after:content-[''] after:flex-1 after:h-px after:bg-tw02-line motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none"><span>or continue with email</span></div>

      <form method="dialog" class="flex flex-col gap-3.5 mb-5">
        <label class="flex flex-col gap-1.5 opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:620ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none">
          <span class="text-[13px] font-semibold text-tw02-sub">Email</span>
          <input type="email" autofocus required autocomplete="email" placeholder="alex@company.com" class="min-h-11 px-3.5 rounded-[9px] border border-tw02-line bg-tw02-hover text-[15px] text-tw02-ink placeholder:text-tw02-sub transition-[border-color,background-color] duration-200 focus:outline-none focus:border-tw02-brandlt focus:bg-tw02-brandwash">
        </label>
        <label class="flex flex-col gap-1.5 opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:680ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none">
          <span class="flex items-baseline justify-between text-[13px] font-semibold text-tw02-sub">Password <a href="#" class="text-xs font-medium text-tw02-brandlt no-underline hover:underline">Forgot?</a></span>
          <input type="password" required autocomplete="current-password" spellcheck="false" class="min-h-11 px-3.5 rounded-[9px] border border-tw02-line bg-tw02-hover text-[15px] text-tw02-ink transition-[border-color,background-color] duration-200 focus:outline-none focus:border-tw02-brandlt focus:bg-tw02-brandwash">
        </label>
        <button type="submit" class="min-h-11 px-5 rounded-[9px] border-0 bg-tw02-brand text-[15px] font-semibold text-white shadow-[0_8px_20px_-6px_var(--color-tw02-glow)] cursor-pointer transition-[translate,box-shadow] duration-200 hover:-translate-y-px hover:shadow-[0_12px_26px_-6px_var(--color-tw02-glow)] focus-visible:outline-2 focus-visible:outline-tw02-brandlt focus-visible:outline-offset-2 opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:750ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none motion-reduce:hover:translate-y-0">Sign in &rarr;</button>
      </form>

      <p class="pt-4 text-center text-sm text-tw02-sub border-t border-tw02-line opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:820ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none">Don't have an account? <a href="#" class="font-semibold text-tw02-brandlt no-underline hover:underline">Sign up</a></p>
    </div>
  </dialog>
</section>
@import "tailwindcss";

@theme {
  --color-tw02-bg: #0a0e1c;
  --color-tw02-bg: oklch(0.16 0.03 267);
  --color-tw02-card: #141a2e;
  --color-tw02-card: oklch(0.22 0.035 267);
  --color-tw02-ink: #f0eeff;
  --color-tw02-ink: oklch(0.94 0.02 290);
  --color-tw02-sub: #9d97c8;
  --color-tw02-sub: oklch(0.71 0.05 290);
  --color-tw02-brand: #7c3aed;
  --color-tw02-brand: oklch(0.54 0.25 296);
  --color-tw02-brandlt: #a78bfa;
  --color-tw02-brandlt: oklch(0.73 0.16 296);
  --color-tw02-brandwash: #7c3aed12;
  --color-tw02-brandwash: oklch(0.54 0.25 296 / 0.08);
  --color-tw02-glow: #7c3aed8c;
  --color-tw02-glow: oklch(0.54 0.25 296 / 0.55);
  --color-tw02-line: #94a3b824;
  --color-tw02-line: oklch(0.7 0.03 255 / 0.16);
  --color-tw02-linehover: #94a3b847;
  --color-tw02-linehover: oklch(0.7 0.03 255 / 0.3);
  --color-tw02-hover: #ffffff08;
  --color-tw02-hover: oklch(1 0 0 / 0.03);
  --color-tw02-hover2: #ffffff10;
  --color-tw02-hover2: oklch(1 0 0 / 0.07);
  --color-tw02-gh: #24292f;
  --color-tw02-gh: oklch(0.28 0.01 265);
  --color-tw02-scrim: #050814b8;
  --color-tw02-scrim: oklch(0.12 0.02 267 / 0.72);
  --font-tw02-sans: 'Outfit', system-ui, sans-serif;
  --animate-tw02-drop: tw02-drop .5s cubic-bezier(.22, 1, .36, 1) both;
}

.tw-02 {
  width: 100%;
}
/* The cascade's keyframes: theme registers the animation, CSS holds the steps. */

@keyframes tw02-drop {
  from {
    opacity: 0;
    filter: blur(8px);
    translate: 0 12px;
  }

  to {
    opacity: 1;
    filter: blur(0);
    translate: 0 0;
  }
}

#tw-02-dlg::backdrop {
  background: oklch(0.12 0.02 267 / 0);
  backdrop-filter: blur(0px);
  transition: background .22s ease, backdrop-filter .22s ease, overlay .22s allow-discrete, display .22s allow-discrete;
}

@starting-style {
  #tw-02-dlg[open]::backdrop {
    background: oklch(0.12 0.02 267 / 0);
    backdrop-filter: blur(0px);
  }
}

@media (prefers-reduced-motion: reduce) {
  #tw-02-dlg::backdrop {
    transition-duration: .001s;
  }
}
(function () {
  var root = document.querySelector('.tw-02');
  var dlg = root.querySelector('#tw-02-dlg');
  var card = dlg.firstElementChild;
  root.querySelector('[data-tw02-open]').addEventListener('click', function () { dlg.showModal(); });
  root.querySelector('[data-tw02-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-tw02-bg: #0a0e1c;
  --color-tw02-bg: oklch(0.16 0.03 267);
  --color-tw02-card: #141a2e;
  --color-tw02-card: oklch(0.22 0.035 267);
  --color-tw02-ink: #f0eeff;
  --color-tw02-ink: oklch(0.94 0.02 290);
  --color-tw02-sub: #9d97c8;
  --color-tw02-sub: oklch(0.71 0.05 290);
  --color-tw02-brand: #7c3aed;
  --color-tw02-brand: oklch(0.54 0.25 296);
  --color-tw02-brandlt: #a78bfa;
  --color-tw02-brandlt: oklch(0.73 0.16 296);
  --color-tw02-brandwash: #7c3aed12;
  --color-tw02-brandwash: oklch(0.54 0.25 296 / 0.08);
  --color-tw02-glow: #7c3aed8c;
  --color-tw02-glow: oklch(0.54 0.25 296 / 0.55);
  --color-tw02-line: #94a3b824;
  --color-tw02-line: oklch(0.7 0.03 255 / 0.16);
  --color-tw02-linehover: #94a3b847;
  --color-tw02-linehover: oklch(0.7 0.03 255 / 0.3);
  --color-tw02-hover: #ffffff08;
  --color-tw02-hover: oklch(1 0 0 / 0.03);
  --color-tw02-hover2: #ffffff10;
  --color-tw02-hover2: oklch(1 0 0 / 0.07);
  --color-tw02-gh: #24292f;
  --color-tw02-gh: oklch(0.28 0.01 265);
  --color-tw02-scrim: #050814b8;
  --color-tw02-scrim: oklch(0.12 0.02 267 / 0.72);
  --font-tw02-sans: 'Outfit', system-ui, sans-serif;
  --animate-tw02-drop: tw02-drop .5s cubic-bezier(.22, 1, .36, 1) both;
}

.tw-02 {
  width: 100%;
}
/* The cascade's keyframes: theme registers the animation, CSS holds the steps. */

@keyframes tw02-drop {
  from {
    opacity: 0;
    filter: blur(8px);
    translate: 0 12px;
  }

  to {
    opacity: 1;
    filter: blur(0);
    translate: 0 0;
  }
}

#tw-02-dlg::backdrop {
  background: oklch(0.12 0.02 267 / 0);
  backdrop-filter: blur(0px);
  transition: background .22s ease, backdrop-filter .22s ease, overlay .22s allow-discrete, display .22s allow-discrete;
}

@starting-style {
  #tw-02-dlg[open]::backdrop {
    background: oklch(0.12 0.02 267 / 0);
    backdrop-filter: blur(0px);
  }
}

@media (prefers-reduced-motion: reduce) {
  #tw-02-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.

  • Eleven nth-child delay rules become eleven [animation-delay:…] utilities on the elements they time. The stylesheet keeps only the keyframes.
  • group-open: on each child reads the <dialog>'s own [open] state, so the cascade restarts on every open — the original needed an .is-open class on an ancestor div to achieve the same thing.
  • The keyframes animate translate, not transform, to stay consistent with v4's individual-property transform utilities.
  • Behaviour change from the CSS original: a native <dialog> supplies the focus trap, Esc, inerting and scroll lock; the controller is 8 lines.
  • UI change: every button and input is min-h-11 (44px), the close ✕ gained a real focus ring, and the social icons keep their provider colours rather than inheriting the theme.
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 Login Modal with Staggered Blur-In Content Drop
Source: https://codefronts.com/components/tailwind-css-modals/tailwind-login-modal-staggered-blur-in-content-drop/

A SaaS sign-in whose shell appears crisp and whose contents develop like a photograph — brand, title, social row, divider, fields and submit each clearing an 8px blur on a staggered offset. The port turns the original's eleven nth-child delay rules into one animate-tw02-drop theme utility plus an [animation-delay:…] on each child.
## HTML
```html
<section class="tw-02 w-full min-h-svh grid place-items-center bg-tw02-bg font-tw02-sans px-6 py-10">
  <button type="button" data-tw02-open class="inline-flex items-center justify-center min-h-11 px-6 rounded-[9px] border-0 text-[15px] font-semibold text-white bg-tw02-brand shadow-[0_8px_20px_-6px_var(--color-tw02-glow)] cursor-pointer transition-[translate,box-shadow] duration-200 hover:-translate-y-px hover:shadow-[0_12px_26px_-6px_var(--color-tw02-glow)] focus-visible:outline-2 focus-visible:outline-tw02-brandlt focus-visible:outline-offset-2 motion-reduce:hover:translate-y-0">Sign in</button>

  <dialog id="tw-02-dlg" aria-labelledby="tw-02-title" class="group m-auto w-[min(420px,94vw)] p-0 border border-tw02-line rounded-2xl text-tw02-ink bg-tw02-card shadow-[0_40px_60px_-20px_#000000a0] opacity-0 transition-[opacity,overlay,display] duration-200 transition-discrete open:opacity-100 open:starting:opacity-0 open:backdrop:bg-tw02-scrim open:backdrop:backdrop-blur-[8px]">
    <div class="relative px-8 pt-9 pb-7">
      <button type="button" data-tw02-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-tw02-sub text-base cursor-pointer transition-colors duration-200 hover:bg-tw02-hover hover:text-tw02-ink focus-visible:outline-2 focus-visible:outline-tw02-brandlt focus-visible:outline-offset-2">&#10005;</button>

      <div class="inline-flex items-center gap-2 mb-6 text-sm font-bold tracking-wide text-tw02-brandlt opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:150ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none">
        <span aria-hidden="true" class="text-sm leading-none">&#9670;</span><span>CodeFronts</span>
      </div>

      <h2 id="tw-02-title" class="mb-1.5 text-[1.6rem] font-extrabold tracking-[-0.02em] leading-tight opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:220ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none">Welcome back</h2>
      <p class="mb-6 text-[15px] text-tw02-sub opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:280ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none">Sign in to your workspace</p>

      <div class="flex flex-col gap-2 mb-[18px]">
        <button type="button" class="inline-flex items-center justify-center gap-2.5 min-h-11 px-3.5 rounded-[9px] border border-tw02-line bg-tw02-hover text-[15px] font-medium text-tw02-ink cursor-pointer transition-colors duration-200 hover:border-tw02-linehover hover:bg-tw02-hover2 focus-visible:outline-2 focus-visible:outline-tw02-brandlt focus-visible:outline-offset-2 opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:350ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none"><span aria-hidden="true" class="grid place-items-center size-5 rounded bg-white text-[0.72rem] font-extrabold text-tw02-bg">G</span>Continue with Google</button>
        <button type="button" class="inline-flex items-center justify-center gap-2.5 min-h-11 px-3.5 rounded-[9px] border border-tw02-line bg-tw02-hover text-[15px] font-medium text-tw02-ink cursor-pointer transition-colors duration-200 hover:border-tw02-linehover hover:bg-tw02-hover2 focus-visible:outline-2 focus-visible:outline-tw02-brandlt focus-visible:outline-offset-2 opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:400ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none"><span aria-hidden="true" class="grid place-items-center size-5 rounded bg-tw02-gh text-[0.85rem] font-extrabold text-white">&#9673;</span>Continue with GitHub</button>
        <button type="button" class="inline-flex items-center justify-center gap-2.5 min-h-11 px-3.5 rounded-[9px] border border-tw02-line bg-tw02-hover text-[15px] font-medium text-tw02-ink cursor-pointer transition-colors duration-200 hover:border-tw02-linehover hover:bg-tw02-hover2 focus-visible:outline-2 focus-visible:outline-tw02-brandlt focus-visible:outline-offset-2 opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:450ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none"><span aria-hidden="true" class="grid place-items-center size-5 rounded bg-tw02-brand text-[0.72rem] font-extrabold text-white">&#9889;</span>SSO / SAML</button>
      </div>

      <div class="flex items-center gap-3 my-4 text-xs tracking-wide text-tw02-sub opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:550ms] before:content-[''] before:flex-1 before:h-px before:bg-tw02-line after:content-[''] after:flex-1 after:h-px after:bg-tw02-line motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none"><span>or continue with email</span></div>

      <form method="dialog" class="flex flex-col gap-3.5 mb-5">
        <label class="flex flex-col gap-1.5 opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:620ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none">
          <span class="text-[13px] font-semibold text-tw02-sub">Email</span>
          <input type="email" autofocus required autocomplete="email" placeholder="alex@company.com" class="min-h-11 px-3.5 rounded-[9px] border border-tw02-line bg-tw02-hover text-[15px] text-tw02-ink placeholder:text-tw02-sub transition-[border-color,background-color] duration-200 focus:outline-none focus:border-tw02-brandlt focus:bg-tw02-brandwash">
        </label>
        <label class="flex flex-col gap-1.5 opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:680ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none">
          <span class="flex items-baseline justify-between text-[13px] font-semibold text-tw02-sub">Password <a href="#" class="text-xs font-medium text-tw02-brandlt no-underline hover:underline">Forgot?</a></span>
          <input type="password" required autocomplete="current-password" spellcheck="false" class="min-h-11 px-3.5 rounded-[9px] border border-tw02-line bg-tw02-hover text-[15px] text-tw02-ink transition-[border-color,background-color] duration-200 focus:outline-none focus:border-tw02-brandlt focus:bg-tw02-brandwash">
        </label>
        <button type="submit" class="min-h-11 px-5 rounded-[9px] border-0 bg-tw02-brand text-[15px] font-semibold text-white shadow-[0_8px_20px_-6px_var(--color-tw02-glow)] cursor-pointer transition-[translate,box-shadow] duration-200 hover:-translate-y-px hover:shadow-[0_12px_26px_-6px_var(--color-tw02-glow)] focus-visible:outline-2 focus-visible:outline-tw02-brandlt focus-visible:outline-offset-2 opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:750ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none motion-reduce:hover:translate-y-0">Sign in &rarr;</button>
      </form>

      <p class="pt-4 text-center text-sm text-tw02-sub border-t border-tw02-line opacity-0 blur-[8px] group-open:animate-tw02-drop [animation-delay:820ms] motion-reduce:animate-none motion-reduce:opacity-100 motion-reduce:blur-none">Don't have an account? <a href="#" class="font-semibold text-tw02-brandlt no-underline hover:underline">Sign up</a></p>
    </div>
  </dialog>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tw02-bg: #0a0e1c;
  --color-tw02-bg: oklch(0.16 0.03 267);
  --color-tw02-card: #141a2e;
  --color-tw02-card: oklch(0.22 0.035 267);
  --color-tw02-ink: #f0eeff;
  --color-tw02-ink: oklch(0.94 0.02 290);
  --color-tw02-sub: #9d97c8;
  --color-tw02-sub: oklch(0.71 0.05 290);
  --color-tw02-brand: #7c3aed;
  --color-tw02-brand: oklch(0.54 0.25 296);
  --color-tw02-brandlt: #a78bfa;
  --color-tw02-brandlt: oklch(0.73 0.16 296);
  --color-tw02-brandwash: #7c3aed12;
  --color-tw02-brandwash: oklch(0.54 0.25 296 / 0.08);
  --color-tw02-glow: #7c3aed8c;
  --color-tw02-glow: oklch(0.54 0.25 296 / 0.55);
  --color-tw02-line: #94a3b824;
  --color-tw02-line: oklch(0.7 0.03 255 / 0.16);
  --color-tw02-linehover: #94a3b847;
  --color-tw02-linehover: oklch(0.7 0.03 255 / 0.3);
  --color-tw02-hover: #ffffff08;
  --color-tw02-hover: oklch(1 0 0 / 0.03);
  --color-tw02-hover2: #ffffff10;
  --color-tw02-hover2: oklch(1 0 0 / 0.07);
  --color-tw02-gh: #24292f;
  --color-tw02-gh: oklch(0.28 0.01 265);
  --color-tw02-scrim: #050814b8;
  --color-tw02-scrim: oklch(0.12 0.02 267 / 0.72);
  --font-tw02-sans: 'Outfit', system-ui, sans-serif;
  --animate-tw02-drop: tw02-drop .5s cubic-bezier(.22, 1, .36, 1) both;
}

.tw-02 {
  width: 100%;
}
/* The cascade's keyframes: theme registers the animation, CSS holds the steps. */

@keyframes tw02-drop {
  from {
    opacity: 0;
    filter: blur(8px);
    translate: 0 12px;
  }

  to {
    opacity: 1;
    filter: blur(0);
    translate: 0 0;
  }
}

#tw-02-dlg::backdrop {
  background: oklch(0.12 0.02 267 / 0);
  backdrop-filter: blur(0px);
  transition: background .22s ease, backdrop-filter .22s ease, overlay .22s allow-discrete, display .22s allow-discrete;
}

@starting-style {
  #tw-02-dlg[open]::backdrop {
    background: oklch(0.12 0.02 267 / 0);
    backdrop-filter: blur(0px);
  }
}

@media (prefers-reduced-motion: reduce) {
  #tw-02-dlg::backdrop {
    transition-duration: .001s;
  }
}
```

## JavaScript
```js
(function () {
  var root = document.querySelector('.tw-02');
  var dlg = root.querySelector('#tw-02-dlg');
  var card = dlg.firstElementChild;
  root.querySelector('[data-tw02-open]').addEventListener('click', function () { dlg.showModal(); });
  root.querySelector('[data-tw02-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

A cascade is not a transition. Eleven elements with eleven offsets need keyframes, so the port keeps them — but as a theme-registered animation. --animate-tw02-drop: tw02-drop .5s cubic-bezier(.22,1,.36,1) both makes animate-tw02-drop a first-class utility, and each child adds only its own [animation-delay:…].

The delay lives with the element. The original's ordering was encoded in :nth-child() selectors, so inserting a field silently reshuffled the timing of everything after it. Here the offset is an attribute of the thing that moves.

The shell stays sharp. Only the contents blur; the card itself does a plain 200ms fade. That is the difference between "a lens focusing on the credentials" and a decorative wobble on a security-sensitive form.

Make it yours

  • Tighten the cascade by scaling every [animation-delay:…] down — 60ms steps read rapid, 120ms steps read ceremonial. Past ~1.2s total it frustrates fast typists.
  • Change the blur depth in the @keyframes block: 4px is barely-there focus, 16px is dramatic (and too much for production auth).
  • Retheme from --color-tw02-brand; the trigger, submit, links and focus states all read it.
  • Swap the password field for a magic-link button — the layout and the cascade are unchanged.

Gotchas — read before shipping

  • autocomplete="email" and autocomplete="current-password" are not optional decoration — password managers key off them, and omitting them costs a large share of mobile sign-ins.
  • animation-delay with both fill means the element is invisible until its turn. Without the motion-reduce: escape hatch, a reduced-motion user with animation: none would be left staring at a permanently hidden form.
  • Arbitrary values must be literal in the markup. Generating [animation-delay:' + n + 'ms] in JS produces a class the compiler never sees, and the element simply never animates.
  • Focus starts on the email field, not the close button — autofocus on the first input is what lets the user type immediately.
  • The social buttons' brand colours are governed by each provider's brand guidelines; recolouring them to match your theme violates those terms.

Browser support

ChromeSafariFirefoxEdge
111+ · 117+ anim16.4+ · 17.5+ anim128+ · 129+ anim111+ · 117+ anim

filter: blur() animation and animation-delay are universal; only the @starting-style shell fade is on the newer gate, and it degrades to an instant show.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Arbitraryneeds bracket syntax

HTML & JSno Tailwind involved

Search CodeFronts

Loading…