36 Tailwind CSS Modals

3 Pure Tailwind33 Tailwind + JSPublished

36 Tailwind CSS modals for auth and password reset, paywalls and subscribe walls, product quick-views and cart drawers, age gates and GDPR consent, delete confirmations and support forms, plus a ⌘K command palette — spanning native <dialog> overlays, :has() and :target zero-JS state machines, slide-over drawers, bottom sheets, and radio-driven tabs. Scoped under .tm-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.

Related22 CSS Progress Bars20 CSS Image Sliders30 Tailwind CSS Login Forms

Premium upgradePartner: Untitled UI

Affiliate link

Building a whole Tailwind app, not just this component?

You just browsed 36 Tailwind modals. Untitled UI Pro unlocks 5,000+ components and sections and 250+ page examples for React, styled with Tailwind CSS v4. One licence is a one-time $349 with lifetime updates; teams start at $999.

Get Untitled UI Pro

Untitled UI React. We may earn a commission, at no extra cost to you.

01. Tailwind Login / Signup Modal with Native Dialog & Starting-Style (opens in a new tab)

Published

Tailwind + JS
The reference-grade auth overlay: one native <dialog> flips between Sign in and Create account with zero-JS tabs, floating labels and a blurring backdrop. The port's win is open: + open:starting: + transition-discrete — the closed state, the open state and the frame the browser paints before the transition all sit in one class list on the element that animates.

02. Tailwind Password Reset Modal with In-Place Success State (opens in a new tab)

Published

Tailwind + JS
A single-input recovery dialog that swaps in place to a neutral 'check your inbox' panel. The port replaces the original's hidden-toggling JavaScript with one data-view attribute on the dialog and two data-[view=…]: variants — the state lives in the DOM where the styles can read it, and the controller shrinks to one assignment.
Newsletter

Enjoying these demos? Get new ones by email.

A short email when a new collection ships. Unsubscribe any time.

at

Almost there: check your inbox to confirm.Manage

Newsletter

Enjoying these demos? Get new ones by email.

A short email when a new collection ships. Unsubscribe any time.

at

Almost there: check your inbox to confirm.Manage

19. Tailwind Confirm Delete Modal with Corner Slide-and-Spin Entrance (opens in a new tab)

Published

Tailwind + JS
A destructive-action confirmation whose card swings in from the bottom-right at 3° and settles square. The port keeps the muted intensity of the original — a spring easing token, not a bounce — and states the whole entrance as open: plus open:starting: on the dialog, so the closed state, the open state and the pre-transition frame are one class list.

20. Tailwind Login Modal with Staggered Blur-In Content Drop (opens in a new tab)

Published

Tailwind + JS
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.

21. Tailwind Signup Modal with 3D Origami Fan Deploy Panels (opens in a new tab)

Published

Tailwind + JS
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.
Newsletter

Enjoying these demos? Get new ones by email.

A short email when a new collection ships. Unsubscribe any time.

at

Almost there: check your inbox to confirm.Manage

Published

Tailwind + JS
A consent centre whose four category rows unfold downward like origami while the title and the three actions are readable from frame one — the fast path stays fast. The port drives the cascade from the dialog's own [open] state with in-[dialog[open]]: plus per-row [transition-delay:…], and builds the toggles from peer-checked: instead of custom controls.

33. Tailwind Order Success Modal with CSS-Only Confetti Burst (opens in a new tab)

Published

Tailwind + JS
The peak-dopamine moment: the card springs from 12% scale while twelve CSS particles fire outward on cos()/sin() paths. No canvas, no particle library — each span carries its own angle, distance and colour as custom properties, and the port keeps them as inline --* values because per-particle randomness is data, not design tokens.
FAQ

Frequently asked questions

How do these Tailwind modals open and animate without a JavaScript animation library?
Every dialog demo opens with the native <dialog> element and its showModal() method, which gives you a real top-layer overlay, a free backdrop via ::backdrop, and Escape-to-close for nothing. Tailwind v4 drives the entrance through the starting: variant, which compiles to a @starting-style block: you write the open state as normal classes (opacity-100 scale-100) and the pre-open state as starting:opacity-0 starting:scale-95, the value the browser reads the instant the element enters the top layer. A transition utility bridges the two. The common trap is the exit: @starting-style only fires on the way in, so a plain close snaps the dialog shut with no fade. You need transition-discrete (which emits transition-behavior: allow-discrete) so the browser keeps the element painted while display and the top-layer membership animate out. That single utility is the difference between a smooth close and a flicker.
How does the age gate and size guide work with zero JavaScript — is :has() really enough?
Yes, and the trick is moving the state up the tree instead of onto the toggled element. The age gate (demo 03) and the cm/inch size toggle (demo 08) both hang a hidden checkbox or radio near the top of the component, then let an ancestor react with has-checked: — Tailwind's compile target for :has(:checked). So the wrapper says has-checked:pointer-events-none has-checked:opacity-0 and the whole gate dismisses when the box flips, no handler required. The unit toggle swaps two columns by pairing peer/has-checked: with hidden so only one measurement set is in flow at a time. Two things to watch: the input must be a real focusable control with a paired <label> so keyboard users can toggle it, and :has() has no old-browser fallback — a visitor on an engine without it sees the gate stuck open. For a hard legal gate that is a reason to keep a server check as well; for a UI convenience it is fine.
My page scrolls behind the modal when it's open — how do I lock the background?
This is the most common modal bug and the native <dialog> only half-solves it. showModal() makes the rest of the document inert, so clicks and Tab are blocked behind the overlay, but it does not stop wheel and touch scrolling of the underlying <body> — the page still moves under the dim backdrop. The drawers and slide-overs here (demos 06 and 15) lock it by toggling overflow-hidden on the root element when the dialog opens and removing it on close. On iOS overflow-hidden alone can still rubber-band; if you see that, also set overscroll-contain on the scrollable panel inside the modal so the scroll chain stops at the panel edge. Avoid position: fixed body hacks — they reset scroll position on close and cause a visible jump. And restore the class in the dialog's close event, not the click handler, so Escape and backdrop dismissal both unlock the page instead of leaving it frozen.
What accessibility do I still have to add — does native dialog handle focus and ARIA for me?
Native <dialog> opened with showModal() gives you a lot: it exposes an implicit role="dialog" with aria-modal, traps Tab inside the open dialog, makes the rest of the page inert, and closes on Escape. What it does not do is label itself or move focus intelligently. Point aria-labelledby at the heading so screen readers announce what opened, and set initial focus on the first meaningful control — the auth modal (demo 01) focuses the email field, the destructive delete (demo 11) focuses the type-to-confirm input, not the dangerous button. On close, return focus to the trigger that opened it, or the user is dumped at the top of the document. For the zero-JS :target lightbox and :has() gates you get none of the native trapping, so those need a real close control in the tab order and a visible focus-visible: ring. Icon-only close buttons always need an aria-label; a bare × is silent.
How does the ⌘K command palette move the selection with arrows but no state variable?
The neat part of demo 18 is that the highlighted row is derived, not stored. Each result is a real button in a list, and the palette uses roving selection: arrow keys move focus with focus() and the active look comes from focus-visible: plus sibling-aware variants, so the browser's own focus is the selection — there is no selectedIndex to keep in sync. Enter runs whatever is focused. Filtering as you type is the only piece that touches JS: it hides non-matching rows with a hidden class rather than removing them, which keeps the DOM order stable so arrow-walking never jumps. Opening on ⌘K means a keydown listener that checks event.metaKey (and ctrlKey for Windows) and calls preventDefault() — browsers bind ⌘K to the address bar, so skip that and the palette never opens. Remember to unbind or guard the listener when the dialog is closed so the shortcut does not fight other views.
When should I use the :target lightbox instead of a JavaScript one?
The :target gallery (demo 09) is genuinely zero-JS: each full-screen frame is an element whose id becomes the URL hash when you click its link, and target: variants show the matching panel while hiding the rest. It is perfect when you want the viewer to be linkable and survive with scripting disabled — a shared URL opens straight to that image. The trade-offs are real, though. Every open and close writes to the browser history, so the back button steps through each frame instead of leaving the page, which surprises people. It also fights other in-page anchors, since any #id navigation now competes for the same mechanism, and it cannot trap focus or mark the background inert the way a native <dialog> does. Use :target for a lightweight, deep-linkable image viewer; reach for the <dialog>-based modals when you need focus management, scroll lock, or a close that does not pollute history.
How do the tabbed settings and multi-step onboarding switch panels without a tab controller?
Two different zero-controller patterns. The settings modal (demo 17) uses radio tabs: one hidden radio group, a <label> for each tab, and the panels shown or hidden with peer/has-checked: variants. Because only one radio in a group can be checked, exactly one panel is ever visible — the browser enforces the mutual exclusion for free, and Tab plus arrow keys already navigate a radio group, so keyboard support comes with it. The onboarding tour (demo 14) is not pure CSS — it keys off a data-step attribute on the dialog and Tailwind's data-[step=2]: style variants, with tiny JS that only increments the attribute on Next. That is deliberate: a three-way step needs a value that can move forward, back, and skip, which radios model awkwardly. The mistake to avoid is styling the panels with hidden only via opacity — a display:none-equivalent (hidden) must win, or a screen reader reads all three panels at once.
Is the native dialog element safe to ship, and when should I not reach for a modal at all?
The native <dialog> with showModal(), ::backdrop, and top-layer rendering is well supported across current engines, and the @starting-style and transition-discrete animation utilities used here are the newer piece — they enhance the entrance but degrade to an instant open on an engine that lacks them, so nothing breaks. The bigger question is whether a modal is the right control. Do not trap something the user needs to reference against the page behind it, and do not modal-gate content a search engine or a linked visitor should reach — the paywall (demo 04) and age gate (demo 03) are intentional friction, but the same pattern applied to ordinary content just hides it. Prefer the slide-over drawer (demo 06) or the filter sheet (demo 15) when the task is a companion to the page rather than a hard interrupt, and inline the GDPR consent as a corner sheet (demo 13), not a blocking overlay, so it never stops a first-time visitor from reading anything.

Related collections

15 CSS 3D Tilt Hover Cards preview

15 CSS 3D Tilt Hover Cards

15 mouse-aware 3D tilt hover cards — e-commerce product spotlight, glassmorphism parallax team card, interactive pricing tier, holographic NFT collectible, pop-out mascot, dark tech grid with border glow, media player album art, blog article preview, cyberpunk neon glow, dashboard KPI widget, minimalist real estate, flip-to-back tilt, mobile app showcase, course learning card, and a Pure CSS touch-friendly tilt. Vanilla JS writes --rx/--ry/--mx/--my; all rendering stays in CSS on the GPU.

24 CSS Animated Cards preview

24 CSS Animated Cards

24 hand-coded CSS animated card patterns organised by what triggers the motion, not by card style. Scroll and entrance triggers: staggered grid reveals, @starting-style mount-in, scroll-driven scale and fade on animation-timeline: view(), View Transitions API card-to-detail morphs, blur-to-sharp lazy image loading, and FLIP re-layout when a filter changes. State-change triggers: add-to-cart success morph, like and save particle burst, expand and collapse to intrinsic height with calc-size(), swipe-to-dismiss on Pointer Events, and skeleton-to-loaded crossfade. Data-driven triggers: count-up KPI metrics, SVG sparkline draw-on-enter, and progress ring fill on load. Ambient idle loops: breathing glow, floating drift, animated gradient mesh backgrounds, conic rotating borders, auto-cycling testimonial decks, and live status pulses. Plus four hover patterns where the effect is the topic itself: cursor spotlight, holographic foil glare, corner ribbon slide, and horizontal accordion expand. Every card is scoped under a .ac-NN prefix for no-collision pasting, guards prefers-reduced-motion, animates compositor-only properties, and ports unchanged to React, Vue, Svelte, Astro, Next.js and Tailwind.

22 CSS Avatars preview

22 CSS Avatars

22 hand-coded CSS avatars for chat apps, team dashboards, comment threads, account menus, and social profiles. Covers circular and squircle shapes, gradient and conic story rings, hexagon clip-path crops, online status dots, notification count badges, verified checkmarks, stacked facepiles with plus-N overflow, initials fallbacks for users without photos, broken-image recovery, avatar pickers, and a size scale system where the ring, badge, and status dot all scale from a single custom property.

Search CodeFronts

Loading…