36 Tailwind CSS Modals
04 / 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 Paywall Modal with Masked Article Fade

A subscribe wall that fades the article tail into nothing with a gradient mask and blurs whatever is left behind a heavy ::backdrop. Two arbitrary properties do the work Tailwind has no scale for — [mask-image:…] and the backdrop composition — and everything else is utilities.

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-04 relative w-full min-h-svh pt-11 px-7 bg-tm04-bg font-tm04-serif text-tm04-ink">
  <article class="max-w-[600px] mx-auto [mask-image:linear-gradient(#000_55%,transparent)]">
    <p class="mb-3.5 font-tm04-sans text-[11px] tracking-[0.22em] text-tm04-acc">MEMBERS &middot; MARKETS</p>
    <h1 class="mb-3.5 text-[clamp(28px,5vw,40px)] leading-[1.15] font-bold text-tm04-hi">The quiet reshaping of global supply chains</h1>
    <p class="mb-6 font-tm04-sans text-[13px] text-tm04-mut">By A. Okafor &middot; 6 min read</p>
    <p class="mb-4.5 text-lg leading-[1.75] text-tm04-body">For three decades the logistics map optimised relentlessly for a single variable: cost. Factories clustered wherever labour was cheapest, and inventory was pared to the hour.</p>
    <p class="mb-4.5 text-lg leading-[1.75] text-tm04-body">Then the shocks arrived in sequence &mdash; a pandemic, a canal, a war &mdash; and the calculus inverted almost overnight. Resilience, not efficiency, became the word every operations chief&hellip;</p>
  </article>

  <div class="absolute inset-x-0 bottom-0 h-[220px] grid place-items-end justify-center pb-10 bg-[linear-gradient(transparent,var(--color-tm04-bg)_62%)]">
    <button type="button" data-tm04-open class="font-tm04-sans inline-flex items-center justify-center min-h-12 px-8 rounded-xl border-0 text-[15px] font-bold cursor-pointer bg-tm04-acc text-tm04-onacc shadow-[0_14px_40px_-12px_var(--color-tm04-acc)] transition-[filter,translate] duration-200 hover:brightness-110 active:translate-y-px focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2 motion-reduce:active:translate-y-0">Subscribe to keep reading</button>
  </div>

  <dialog id="tm-04-dlg" aria-labelledby="tm-04-title" class="m-auto w-[min(420px,94vw)] p-0 border-0 rounded-[20px] font-tm04-sans text-tm04-ink bg-tm04-card shadow-[0_40px_90px_-30px_#000,0_0_0_1px_var(--color-tm04-edge)] opacity-0 translate-y-3.5 scale-[0.97] transition-[opacity,translate,scale,overlay,display] duration-300 transition-discrete open:opacity-100 open:translate-y-0 open:scale-100 open:starting:opacity-0 open:starting:translate-y-3.5 open:starting:scale-[0.97] open:backdrop:bg-tm04-scrim open:backdrop:backdrop-blur-[9px] motion-reduce:transition-none">
    <div class="relative px-8 pt-8 pb-6">
      <button type="button" data-tm04-close aria-label="Close" class="absolute top-3.5 right-3.5 grid place-items-center size-9 rounded-lg border-0 bg-transparent text-tm04-mut text-[22px] leading-none cursor-pointer transition-colors duration-200 hover:bg-tm04-well hover:text-white focus-visible:outline-2 focus-visible:outline-tm04-acc focus-visible:outline-offset-2">&times;</button>
      <p class="mb-2.5 text-[11px] uppercase tracking-[0.2em] text-tm04-acc">Unlimited access</p>
      <h2 id="tm-04-title" class="mb-5 text-2xl font-extrabold tracking-[-0.01em] text-tm04-hi">Keep reading &mdash; join today</h2>
      <ul class="flex flex-col gap-3 mb-5 list-none p-0">
        <li class="relative pl-7 text-[14.5px] text-tm04-body before:content-['✓'] before:absolute before:left-0 before:top-0 before:font-extrabold before:text-tm04-acc">Every members-only report &amp; analysis</li>
        <li class="relative pl-7 text-[14.5px] text-tm04-body before:content-['✓'] before:absolute before:left-0 before:top-0 before:font-extrabold before:text-tm04-acc">Ad-free reading across all devices</li>
        <li class="relative pl-7 text-[14.5px] text-tm04-body before:content-['✓'] before:absolute before:left-0 before:top-0 before:font-extrabold before:text-tm04-acc">Cancel anytime, no questions asked</li>
      </ul>
      <div class="flex items-baseline gap-1.5 mb-4"><b class="text-[34px] text-white">$8</b><span class="text-sm text-tm04-mut">/ month</span></div>
      <button type="button" class="w-full min-h-12 rounded-xl border-0 bg-tm04-acc text-[15px] font-bold text-tm04-onacc cursor-pointer transition-[filter,translate] duration-200 hover:brightness-110 active:translate-y-px focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2 motion-reduce:active:translate-y-0">Subscribe now</button>
      <p class="mt-4 text-center text-[13px] text-tm04-mut">Already a member? <a href="#login" class="font-semibold text-tm04-acc no-underline hover:underline focus-visible:outline-2 focus-visible:outline-tm04-acc focus-visible:outline-offset-2">Log in</a></p>
      <button type="button" data-tm04-later class="block mx-auto mt-3 min-h-11 px-3 rounded-md border-0 bg-transparent text-[13px] text-tm04-faint cursor-pointer transition-colors duration-200 hover:text-tm04-mut focus-visible:outline-2 focus-visible:outline-tm04-acc focus-visible:outline-offset-2">Maybe later</button>
    </div>
  </dialog>
</section>
@import "tailwindcss";

@theme {
  --color-tm04-bg: #0c0d12;
  --color-tm04-bg: oklch(0.16 0.012 285);
  --color-tm04-card: #181a26;
  --color-tm04-card: oklch(0.23 0.02 285);
  --color-tm04-well: #212436;
  --color-tm04-well: oklch(0.27 0.025 285);
  --color-tm04-edge: #262a3a;
  --color-tm04-edge: oklch(0.3 0.02 285);
  --color-tm04-hi: #f3f4fa;
  --color-tm04-hi: oklch(0.97 0.005 285);
  --color-tm04-ink: #e8e9f0;
  --color-tm04-ink: oklch(0.93 0.008 285);
  --color-tm04-body: #c3c6d4;
  --color-tm04-body: oklch(0.83 0.015 285);
  --color-tm04-mut: #9599aa;
  --color-tm04-mut: oklch(0.7 0.02 285);
  --color-tm04-faint: #70748a;
  --color-tm04-faint: oklch(0.56 0.02 285);
  --color-tm04-acc: #b98cff;
  --color-tm04-acc: oklch(0.72 0.19 300);
  --color-tm04-onacc: #120820;
  --color-tm04-onacc: oklch(0.17 0.05 300);
  --color-tm04-scrim: #08090e80;
  --color-tm04-scrim: oklch(0.12 0.01 285 / 0.5);
  --font-tm04-serif: 'Newsreader', Georgia, 'Times New Roman', serif;
  --font-tm04-sans: 'Space Grotesk', system-ui, sans-serif;
}

.tm-04 {
  width: 100%;
}
/* Safari < 18 mask prefix — the utility emits the unprefixed property only. */

.tm-04 article {
  -webkit-mask-image: linear-gradient(#000 55%, transparent);
}

#tm-04-dlg::backdrop {
  background: oklch(0.12 0.01 285 / 0);
  backdrop-filter: blur(0px);
  transition: background .35s, backdrop-filter .35s, overlay .35s allow-discrete, display .35s allow-discrete;
}

@starting-style {
  #tm-04-dlg[open]::backdrop {
    background: oklch(0.12 0.01 285 / 0);
    backdrop-filter: blur(0px);
  }
}
(function () {
  var root = document.querySelector('.tm-04');
  var dlg = root.querySelector('#tm-04-dlg');
  root.querySelector('[data-tm04-open]').addEventListener('click', function () { dlg.showModal(); });
  root.querySelector('[data-tm04-close]').addEventListener('click', function () { dlg.close(); });
  root.querySelector('[data-tm04-later]').addEventListener('click', function () { 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-tm04-bg: #0c0d12;
  --color-tm04-bg: oklch(0.16 0.012 285);
  --color-tm04-card: #181a26;
  --color-tm04-card: oklch(0.23 0.02 285);
  --color-tm04-well: #212436;
  --color-tm04-well: oklch(0.27 0.025 285);
  --color-tm04-edge: #262a3a;
  --color-tm04-edge: oklch(0.3 0.02 285);
  --color-tm04-hi: #f3f4fa;
  --color-tm04-hi: oklch(0.97 0.005 285);
  --color-tm04-ink: #e8e9f0;
  --color-tm04-ink: oklch(0.93 0.008 285);
  --color-tm04-body: #c3c6d4;
  --color-tm04-body: oklch(0.83 0.015 285);
  --color-tm04-mut: #9599aa;
  --color-tm04-mut: oklch(0.7 0.02 285);
  --color-tm04-faint: #70748a;
  --color-tm04-faint: oklch(0.56 0.02 285);
  --color-tm04-acc: #b98cff;
  --color-tm04-acc: oklch(0.72 0.19 300);
  --color-tm04-onacc: #120820;
  --color-tm04-onacc: oklch(0.17 0.05 300);
  --color-tm04-scrim: #08090e80;
  --color-tm04-scrim: oklch(0.12 0.01 285 / 0.5);
  --font-tm04-serif: 'Newsreader', Georgia, 'Times New Roman', serif;
  --font-tm04-sans: 'Space Grotesk', system-ui, sans-serif;
}

.tm-04 {
  width: 100%;
}
/* Safari < 18 mask prefix — the utility emits the unprefixed property only. */

.tm-04 article {
  -webkit-mask-image: linear-gradient(#000 55%, transparent);
}

#tm-04-dlg::backdrop {
  background: oklch(0.12 0.01 285 / 0);
  backdrop-filter: blur(0px);
  transition: background .35s, backdrop-filter .35s, overlay .35s allow-discrete, display .35s allow-discrete;
}

@starting-style {
  #tm-04-dlg[open]::backdrop {
    background: oklch(0.12 0.01 285 / 0);
    backdrop-filter: blur(0px);
  }
}
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&family=Newsreader:opsz,wght@6..72,400;6..72,600;6..72,700&display=swap">

What changed in the port

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

  • The gradient overlay that grounded the CTA became a single arbitrary background utility reading the same --color-tm04-bg token as the page, so the fade can never drift from the page colour — the CSS version hard-coded #0c0d12 twice.
  • The checkmark bullets are before:content-['✓'] per item rather than one li::before rule. More characters, but a list item without the benefit styling is now visibly different in the markup.
  • UI change from the CSS original: the article is set in Newsreader rather than Georgia (the serif the mask fade was designed around), 'Maybe later' grew from a 6px-padded text button to a 44px target, and the login link gained a focus ring it did not have.
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 Paywall Modal with Masked Article Fade
Source: https://codefronts.com/components/tailwind-css-modals/tailwind-paywall-modal-masked-article-fade/

A subscribe wall that fades the article tail into nothing with a gradient mask and blurs whatever is left behind a heavy ::backdrop. Two arbitrary properties do the work Tailwind has no scale for — [mask-image:…] and the backdrop composition — and everything else is utilities.
## HTML
```html
<section class="tm-04 relative w-full min-h-svh pt-11 px-7 bg-tm04-bg font-tm04-serif text-tm04-ink">
  <article class="max-w-[600px] mx-auto [mask-image:linear-gradient(#000_55%,transparent)]">
    <p class="mb-3.5 font-tm04-sans text-[11px] tracking-[0.22em] text-tm04-acc">MEMBERS &middot; MARKETS</p>
    <h1 class="mb-3.5 text-[clamp(28px,5vw,40px)] leading-[1.15] font-bold text-tm04-hi">The quiet reshaping of global supply chains</h1>
    <p class="mb-6 font-tm04-sans text-[13px] text-tm04-mut">By A. Okafor &middot; 6 min read</p>
    <p class="mb-4.5 text-lg leading-[1.75] text-tm04-body">For three decades the logistics map optimised relentlessly for a single variable: cost. Factories clustered wherever labour was cheapest, and inventory was pared to the hour.</p>
    <p class="mb-4.5 text-lg leading-[1.75] text-tm04-body">Then the shocks arrived in sequence &mdash; a pandemic, a canal, a war &mdash; and the calculus inverted almost overnight. Resilience, not efficiency, became the word every operations chief&hellip;</p>
  </article>

  <div class="absolute inset-x-0 bottom-0 h-[220px] grid place-items-end justify-center pb-10 bg-[linear-gradient(transparent,var(--color-tm04-bg)_62%)]">
    <button type="button" data-tm04-open class="font-tm04-sans inline-flex items-center justify-center min-h-12 px-8 rounded-xl border-0 text-[15px] font-bold cursor-pointer bg-tm04-acc text-tm04-onacc shadow-[0_14px_40px_-12px_var(--color-tm04-acc)] transition-[filter,translate] duration-200 hover:brightness-110 active:translate-y-px focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2 motion-reduce:active:translate-y-0">Subscribe to keep reading</button>
  </div>

  <dialog id="tm-04-dlg" aria-labelledby="tm-04-title" class="m-auto w-[min(420px,94vw)] p-0 border-0 rounded-[20px] font-tm04-sans text-tm04-ink bg-tm04-card shadow-[0_40px_90px_-30px_#000,0_0_0_1px_var(--color-tm04-edge)] opacity-0 translate-y-3.5 scale-[0.97] transition-[opacity,translate,scale,overlay,display] duration-300 transition-discrete open:opacity-100 open:translate-y-0 open:scale-100 open:starting:opacity-0 open:starting:translate-y-3.5 open:starting:scale-[0.97] open:backdrop:bg-tm04-scrim open:backdrop:backdrop-blur-[9px] motion-reduce:transition-none">
    <div class="relative px-8 pt-8 pb-6">
      <button type="button" data-tm04-close aria-label="Close" class="absolute top-3.5 right-3.5 grid place-items-center size-9 rounded-lg border-0 bg-transparent text-tm04-mut text-[22px] leading-none cursor-pointer transition-colors duration-200 hover:bg-tm04-well hover:text-white focus-visible:outline-2 focus-visible:outline-tm04-acc focus-visible:outline-offset-2">&times;</button>
      <p class="mb-2.5 text-[11px] uppercase tracking-[0.2em] text-tm04-acc">Unlimited access</p>
      <h2 id="tm-04-title" class="mb-5 text-2xl font-extrabold tracking-[-0.01em] text-tm04-hi">Keep reading &mdash; join today</h2>
      <ul class="flex flex-col gap-3 mb-5 list-none p-0">
        <li class="relative pl-7 text-[14.5px] text-tm04-body before:content-['✓'] before:absolute before:left-0 before:top-0 before:font-extrabold before:text-tm04-acc">Every members-only report &amp; analysis</li>
        <li class="relative pl-7 text-[14.5px] text-tm04-body before:content-['✓'] before:absolute before:left-0 before:top-0 before:font-extrabold before:text-tm04-acc">Ad-free reading across all devices</li>
        <li class="relative pl-7 text-[14.5px] text-tm04-body before:content-['✓'] before:absolute before:left-0 before:top-0 before:font-extrabold before:text-tm04-acc">Cancel anytime, no questions asked</li>
      </ul>
      <div class="flex items-baseline gap-1.5 mb-4"><b class="text-[34px] text-white">$8</b><span class="text-sm text-tm04-mut">/ month</span></div>
      <button type="button" class="w-full min-h-12 rounded-xl border-0 bg-tm04-acc text-[15px] font-bold text-tm04-onacc cursor-pointer transition-[filter,translate] duration-200 hover:brightness-110 active:translate-y-px focus-visible:outline-2 focus-visible:outline-white focus-visible:outline-offset-2 motion-reduce:active:translate-y-0">Subscribe now</button>
      <p class="mt-4 text-center text-[13px] text-tm04-mut">Already a member? <a href="#login" class="font-semibold text-tm04-acc no-underline hover:underline focus-visible:outline-2 focus-visible:outline-tm04-acc focus-visible:outline-offset-2">Log in</a></p>
      <button type="button" data-tm04-later class="block mx-auto mt-3 min-h-11 px-3 rounded-md border-0 bg-transparent text-[13px] text-tm04-faint cursor-pointer transition-colors duration-200 hover:text-tm04-mut focus-visible:outline-2 focus-visible:outline-tm04-acc focus-visible:outline-offset-2">Maybe later</button>
    </div>
  </dialog>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tm04-bg: #0c0d12;
  --color-tm04-bg: oklch(0.16 0.012 285);
  --color-tm04-card: #181a26;
  --color-tm04-card: oklch(0.23 0.02 285);
  --color-tm04-well: #212436;
  --color-tm04-well: oklch(0.27 0.025 285);
  --color-tm04-edge: #262a3a;
  --color-tm04-edge: oklch(0.3 0.02 285);
  --color-tm04-hi: #f3f4fa;
  --color-tm04-hi: oklch(0.97 0.005 285);
  --color-tm04-ink: #e8e9f0;
  --color-tm04-ink: oklch(0.93 0.008 285);
  --color-tm04-body: #c3c6d4;
  --color-tm04-body: oklch(0.83 0.015 285);
  --color-tm04-mut: #9599aa;
  --color-tm04-mut: oklch(0.7 0.02 285);
  --color-tm04-faint: #70748a;
  --color-tm04-faint: oklch(0.56 0.02 285);
  --color-tm04-acc: #b98cff;
  --color-tm04-acc: oklch(0.72 0.19 300);
  --color-tm04-onacc: #120820;
  --color-tm04-onacc: oklch(0.17 0.05 300);
  --color-tm04-scrim: #08090e80;
  --color-tm04-scrim: oklch(0.12 0.01 285 / 0.5);
  --font-tm04-serif: 'Newsreader', Georgia, 'Times New Roman', serif;
  --font-tm04-sans: 'Space Grotesk', system-ui, sans-serif;
}

.tm-04 {
  width: 100%;
}
/* Safari < 18 mask prefix — the utility emits the unprefixed property only. */

.tm-04 article {
  -webkit-mask-image: linear-gradient(#000 55%, transparent);
}

#tm-04-dlg::backdrop {
  background: oklch(0.12 0.01 285 / 0);
  backdrop-filter: blur(0px);
  transition: background .35s, backdrop-filter .35s, overlay .35s allow-discrete, display .35s allow-discrete;
}

@starting-style {
  #tm-04-dlg[open]::backdrop {
    background: oklch(0.12 0.01 285 / 0);
    backdrop-filter: blur(0px);
  }
}
```

## JavaScript
```js
(function () {
  var root = document.querySelector('.tm-04');
  var dlg = root.querySelector('#tm-04-dlg');
  root.querySelector('[data-tm04-open]').addEventListener('click', function () { dlg.showModal(); });
  root.querySelector('[data-tm04-close]').addEventListener('click', function () { dlg.close(); });
  root.querySelector('[data-tm04-later]').addEventListener('click', function () { dlg.close(); });
})();
```

How this works

Two blurs, two jobs. The mask fades the article's tail so the reader hits a wall before the modal ever opens; the ::backdrop blur then covers what remains once the wall is up. Keeping them separate means the page still reads correctly with the dialog closed — which is the metered-content experience, not a bug.

Arbitrary properties are the right answer here. A mask gradient is not a design-scale value; there is no sensible mask-* utility to mint. [mask-image:linear-gradient(#000_55%,transparent)] says it once, on the element, with the underscore escape carrying the multi-value syntax intact.

The offer card is ordinary layout. Eyebrow, heading, three benefits, a price row with items-baseline, a full-width CTA, a login link and a low-emphasis dismiss — no technique, just hierarchy.

Make it yours

  • Move the wall by editing one number: #000_55% → #000_70% shows more article.
  • Swap the blur for a solid scrim: drop backdrop-blur-[9px] and raise the scrim token's alpha.
  • Wire the CTA to checkout; keep 'Already a member? Log in' visible — it is the highest-intent link on the card.

Gotchas — read before shipping

  • For SEO, do not display:none the gated copy. Serve the full text and gate it visually (Google's flexible sampling), or ship paywall structured data.
  • A blur is a visual lock, not a security boundary — the text is in the DOM. Enforce access server-side.
  • Safari below 18 needs -webkit-mask-image; that is why the stylesheet restates the mask instead of relying on the utility alone.
  • Decide deliberately whether Esc dismisses. A hard wall may omit the close, but that trades away a key accessibility affordance — document the choice.

Browser support

ChromeSafariFirefoxEdge
111+ · 117+ anim16.4+ (mask prefixed) · 18+ unprefixed128+111+ · 117+ anim

mask-image and backdrop-filter are the gate; both degrade to a hard cut and an unblurred backdrop, which is still a usable wall.

Techniques used in this demo

HTML & JSno Tailwind involved

Search CodeFronts

Loading…