36 Tailwind CSS Modals
03 / 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
Pure TailwindMIT licensed

Tailwind Age Verification Gate with :has() and Zero JavaScript

A full-screen entry gate that blurs and blocks the page until the visitor confirms their age — still zero JavaScript, now keyboard-operable. The port reads one checkbox from two directions: has-checked: on the wrapper un-blurs the article, and peer-checked: on the gate fades it out.

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="tm-03 group relative w-full min-h-svh overflow-hidden bg-tm03-bg font-tm03-sans">
  <article class="min-h-svh flex flex-col items-center justify-center gap-5 p-10 text-center blur-[9px] scale-[1.02] transition-[filter,transform] duration-500 group-has-checked:blur-none group-has-checked:scale-100 motion-reduce:transition-none">
    <h1 class="text-[clamp(38px,9vw,74px)] font-extrabold tracking-[0.12em] text-tm03-hi">CASK &amp; OAK</h1>
    <p class="max-w-[44ch] text-base leading-relaxed text-tm03-mut">Small-batch single malt, aged in highland oak. Explore the reserve collection.</p>
    <div aria-hidden="true" class="flex gap-4 mt-2">
      <span class="w-[90px] h-[120px] rounded-xl border border-tm03-edge bg-tm03-tile"></span>
      <span class="w-[90px] h-[120px] rounded-xl border border-tm03-edge bg-tm03-tile"></span>
      <span class="w-[90px] h-[120px] rounded-xl border border-tm03-edge bg-tm03-tile"></span>
    </div>
  </article>

  <input type="checkbox" id="tm-03-ok" class="peer sr-only">
  <div role="dialog" aria-modal="true" aria-labelledby="tm-03-t" aria-describedby="tm-03-d" class="fixed inset-0 z-10 grid place-items-center p-6 bg-tm03-scrim backdrop-blur-[3px] transition-[opacity,visibility] duration-[450ms] peer-checked:opacity-0 peer-checked:invisible motion-reduce:transition-none">
    <div class="w-[min(430px,94vw)] px-8 py-10 text-center rounded-[20px] bg-tm03-panel border border-tm03-edge shadow-[0_40px_90px_-30px_#000]">
      <div aria-hidden="true" class="grid place-items-center size-20 mx-auto mb-5 rounded-full border-2 border-tm03-accedge text-[26px] font-extrabold text-tm03-acc shadow-[0_0_40px_-10px_var(--color-tm03-acc),inset_0_0_24px_-12px_var(--color-tm03-acc)]">21+</div>
      <h2 id="tm-03-t" class="mb-2.5 text-[22px] font-bold text-tm03-hi">Are you of legal drinking age?</h2>
      <p id="tm-03-d" class="mb-6 text-sm text-tm03-mut">You must be 21 or older to enter this site.</p>
      <div class="flex flex-wrap justify-center gap-3">
        <label for="tm-03-ok" class="inline-flex items-center justify-center min-h-11 px-6 rounded-xl text-sm font-semibold cursor-pointer bg-tm03-acc text-tm03-onacc border border-tm03-acc transition-[filter] duration-200 hover:brightness-110 group-has-[#tm-03-ok:focus-visible]:outline-2 group-has-[#tm-03-ok:focus-visible]:outline-tm03-acc group-has-[#tm-03-ok:focus-visible]:outline-offset-[3px]">Yes, I am 21+</label>
        <a href="https://responsibility.org" class="inline-flex items-center justify-center min-h-11 px-6 rounded-xl text-sm font-semibold no-underline text-tm03-mut border border-tm03-edge transition-colors duration-200 hover:text-tm03-ink hover:border-tm03-accedge focus-visible:outline-2 focus-visible:outline-tm03-acc focus-visible:outline-offset-[3px]">No, exit</a>
      </div>
      <p class="mt-5 text-xs tracking-[0.06em] text-tm03-faint">Press <kbd class="px-1.5 py-0.5 rounded border border-tm03-edge text-[11px]">Space</kbd> to confirm &middot; please enjoy responsibly.</p>
    </div>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-tm03-bg: #0b0805;
  --color-tm03-bg: oklch(0.15 0.015 70);
  --color-tm03-panel: #1a1309;
  --color-tm03-panel: oklch(0.21 0.025 70);
  --color-tm03-tile: #241a0f;
  --color-tm03-tile: oklch(0.25 0.03 70);
  --color-tm03-edge: #3a2c1a;
  --color-tm03-edge: oklch(0.32 0.035 70);
  --color-tm03-hi: #f3e9d8;
  --color-tm03-hi: oklch(0.93 0.025 80);
  --color-tm03-ink: #d9cdb8;
  --color-tm03-ink: oklch(0.85 0.03 80);
  --color-tm03-mut: #b6a894;
  --color-tm03-mut: oklch(0.73 0.03 80);
  --color-tm03-faint: #8a7b64;
  --color-tm03-faint: oklch(0.6 0.03 80);
  --color-tm03-acc: #e0a04d;
  --color-tm03-acc: oklch(0.76 0.13 70);
  --color-tm03-accedge: #a87a3c;
  --color-tm03-accedge: oklch(0.6 0.1 70);
  --color-tm03-onacc: #1c1206;
  --color-tm03-onacc: oklch(0.2 0.03 70);
  --color-tm03-scrim: #080604c7;
  --color-tm03-scrim: oklch(0.13 0.012 70 / 0.78);
  --font-tm03-sans: 'Space Grotesk', system-ui, sans-serif;
}

.tm-03 {
  width: 100%;
}

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-tm03-bg: #0b0805;
  --color-tm03-bg: oklch(0.15 0.015 70);
  --color-tm03-panel: #1a1309;
  --color-tm03-panel: oklch(0.21 0.025 70);
  --color-tm03-tile: #241a0f;
  --color-tm03-tile: oklch(0.25 0.03 70);
  --color-tm03-edge: #3a2c1a;
  --color-tm03-edge: oklch(0.32 0.035 70);
  --color-tm03-hi: #f3e9d8;
  --color-tm03-hi: oklch(0.93 0.025 80);
  --color-tm03-ink: #d9cdb8;
  --color-tm03-ink: oklch(0.85 0.03 80);
  --color-tm03-mut: #b6a894;
  --color-tm03-mut: oklch(0.73 0.03 80);
  --color-tm03-faint: #8a7b64;
  --color-tm03-faint: oklch(0.6 0.03 80);
  --color-tm03-acc: #e0a04d;
  --color-tm03-acc: oklch(0.76 0.13 70);
  --color-tm03-accedge: #a87a3c;
  --color-tm03-accedge: oklch(0.6 0.1 70);
  --color-tm03-onacc: #1c1206;
  --color-tm03-onacc: oklch(0.2 0.03 70);
  --color-tm03-scrim: #080604c7;
  --color-tm03-scrim: oklch(0.13 0.012 70 / 0.78);
  --font-tm03-sans: 'Space Grotesk', system-ui, sans-serif;
}

.tm-03 {
  width: 100%;
}
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=Space+Grotesk:wght@400;500;600;700&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • .cm-03:has(#cm-03-ok:checked) .cm-03__content and its twin for the gate become group-has-checked: and peer-checked: — two variants, no ids in selectors, and each sits on the element it changes.
  • UI change from the CSS original: the confirm control is no longer a role="button" label with a dead Enter key. The real checkbox stays focusable via sr-only, Space confirms, the ring renders on the visible button through peer-focus-visible:, and a hint line says so.
  • Both buttons moved to min-h-11, and the gate's exit is opacity + visibility rather than visibility alone, so the dismissal reads as a transition instead of a cut.
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 Age Verification Gate with :has() and Zero JavaScript
Source: https://codefronts.com/components/tailwind-css-modals/tailwind-age-verification-gate-has-checked-no-javascript/

A full-screen entry gate that blurs and blocks the page until the visitor confirms their age — still zero JavaScript, now keyboard-operable. The port reads one checkbox from two directions: has-checked: on the wrapper un-blurs the article, and peer-checked: on the gate fades it out.
## HTML
```html
<section class="tm-03 group relative w-full min-h-svh overflow-hidden bg-tm03-bg font-tm03-sans">
  <article class="min-h-svh flex flex-col items-center justify-center gap-5 p-10 text-center blur-[9px] scale-[1.02] transition-[filter,transform] duration-500 group-has-checked:blur-none group-has-checked:scale-100 motion-reduce:transition-none">
    <h1 class="text-[clamp(38px,9vw,74px)] font-extrabold tracking-[0.12em] text-tm03-hi">CASK &amp; OAK</h1>
    <p class="max-w-[44ch] text-base leading-relaxed text-tm03-mut">Small-batch single malt, aged in highland oak. Explore the reserve collection.</p>
    <div aria-hidden="true" class="flex gap-4 mt-2">
      <span class="w-[90px] h-[120px] rounded-xl border border-tm03-edge bg-tm03-tile"></span>
      <span class="w-[90px] h-[120px] rounded-xl border border-tm03-edge bg-tm03-tile"></span>
      <span class="w-[90px] h-[120px] rounded-xl border border-tm03-edge bg-tm03-tile"></span>
    </div>
  </article>

  <input type="checkbox" id="tm-03-ok" class="peer sr-only">
  <div role="dialog" aria-modal="true" aria-labelledby="tm-03-t" aria-describedby="tm-03-d" class="fixed inset-0 z-10 grid place-items-center p-6 bg-tm03-scrim backdrop-blur-[3px] transition-[opacity,visibility] duration-[450ms] peer-checked:opacity-0 peer-checked:invisible motion-reduce:transition-none">
    <div class="w-[min(430px,94vw)] px-8 py-10 text-center rounded-[20px] bg-tm03-panel border border-tm03-edge shadow-[0_40px_90px_-30px_#000]">
      <div aria-hidden="true" class="grid place-items-center size-20 mx-auto mb-5 rounded-full border-2 border-tm03-accedge text-[26px] font-extrabold text-tm03-acc shadow-[0_0_40px_-10px_var(--color-tm03-acc),inset_0_0_24px_-12px_var(--color-tm03-acc)]">21+</div>
      <h2 id="tm-03-t" class="mb-2.5 text-[22px] font-bold text-tm03-hi">Are you of legal drinking age?</h2>
      <p id="tm-03-d" class="mb-6 text-sm text-tm03-mut">You must be 21 or older to enter this site.</p>
      <div class="flex flex-wrap justify-center gap-3">
        <label for="tm-03-ok" class="inline-flex items-center justify-center min-h-11 px-6 rounded-xl text-sm font-semibold cursor-pointer bg-tm03-acc text-tm03-onacc border border-tm03-acc transition-[filter] duration-200 hover:brightness-110 group-has-[#tm-03-ok:focus-visible]:outline-2 group-has-[#tm-03-ok:focus-visible]:outline-tm03-acc group-has-[#tm-03-ok:focus-visible]:outline-offset-[3px]">Yes, I am 21+</label>
        <a href="https://responsibility.org" class="inline-flex items-center justify-center min-h-11 px-6 rounded-xl text-sm font-semibold no-underline text-tm03-mut border border-tm03-edge transition-colors duration-200 hover:text-tm03-ink hover:border-tm03-accedge focus-visible:outline-2 focus-visible:outline-tm03-acc focus-visible:outline-offset-[3px]">No, exit</a>
      </div>
      <p class="mt-5 text-xs tracking-[0.06em] text-tm03-faint">Press <kbd class="px-1.5 py-0.5 rounded border border-tm03-edge text-[11px]">Space</kbd> to confirm &middot; please enjoy responsibly.</p>
    </div>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tm03-bg: #0b0805;
  --color-tm03-bg: oklch(0.15 0.015 70);
  --color-tm03-panel: #1a1309;
  --color-tm03-panel: oklch(0.21 0.025 70);
  --color-tm03-tile: #241a0f;
  --color-tm03-tile: oklch(0.25 0.03 70);
  --color-tm03-edge: #3a2c1a;
  --color-tm03-edge: oklch(0.32 0.035 70);
  --color-tm03-hi: #f3e9d8;
  --color-tm03-hi: oklch(0.93 0.025 80);
  --color-tm03-ink: #d9cdb8;
  --color-tm03-ink: oklch(0.85 0.03 80);
  --color-tm03-mut: #b6a894;
  --color-tm03-mut: oklch(0.73 0.03 80);
  --color-tm03-faint: #8a7b64;
  --color-tm03-faint: oklch(0.6 0.03 80);
  --color-tm03-acc: #e0a04d;
  --color-tm03-acc: oklch(0.76 0.13 70);
  --color-tm03-accedge: #a87a3c;
  --color-tm03-accedge: oklch(0.6 0.1 70);
  --color-tm03-onacc: #1c1206;
  --color-tm03-onacc: oklch(0.2 0.03 70);
  --color-tm03-scrim: #080604c7;
  --color-tm03-scrim: oklch(0.13 0.012 70 / 0.78);
  --font-tm03-sans: 'Space Grotesk', system-ui, sans-serif;
}

.tm-03 {
  width: 100%;
}
```

How this works

One checkbox, read from both ends. The hidden input sits between the article and the gate. The wrapper carries group-free has-checked: utilities on the article (un-blur), while the gate is the checkbox's peer (fade out). Neither needs a selector written anywhere but on the element it affects.

The keyboard fix is the port's real change. The CSS original used <label role="button" tabindex="0"> — clickable, but Enter does nothing on a label, so keyboard users were stuck. Here the checkbox itself stays in the tab order (sr-only is focusable; hidden would not be), Space confirms, and peer-focus-visible: paints the ring on the visible button.

The blur is a tease, not a lock. blur-[9px] scale-[1.02] on the article keeps the layout intact while making the copy unreadable; the slight scale hides the blur's soft edges at the viewport boundary.

Make it yours

  • Swap the 21+ mark and copy for your jurisdiction; the ring is size-20 rounded-full border-2.
  • Persist the answer with three lines of JS writing localStorage and setting checked on load.
  • Replace yes/no with a date-of-birth <form> if your regulator requires it — then the gate needs JS and should become a real <dialog>.

Gotchas — read before shipping

  • A CSS gate is not a focus trap: Tab still reaches the blurred article behind it. For legally binding gates use showModal() (demos 01 and 11), which inerts the background.
  • Never use hidden or display:none on the driving checkbox — it leaves the tab order and the gate becomes mouse-only again. sr-only is the only correct hiding utility here.
  • This blocks rendering, not data. The content is in the DOM; it is a UX gate, never a security boundary.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

:has() drives the un-blur (Firefox 121). Without it the article stays blurred behind a dismissible gate, so pair with a JS fallback if you must support older engines.

Techniques used in this demo

HTML & JSno Tailwind involved

Search CodeFronts

Loading…