A Modal / Dialog

Build a centered modal with a backdrop — CSS-only for demos, native <dialog> for production.

A modal is really three things: a trigger, a full-screen backdrop, and a centered panel. Tailwind makes each trivial; the interesting part is doing it accessibly.

The layout recipe

  • Backdrop: fixed inset-0 bg-black/50 covers the viewport.
  • Centering: make the backdrop a grid place-items-center and drop the panel inside — perfectly centered, no transforms.
  • Panel: bg-white rounded-2xl shadow-xl w-full max-w-md p-6.

A no-JavaScript version

For a demo, a hidden checkbox toggles everything through peer-checked:. Great for teaching, and it genuinely runs with zero script.

What production actually wants

The honest senior note: use the native <dialog> element. It gives you focus trapping, Esc to close, a real backdrop, and inertness of the page behind — accessibility you should not hand-roll. Then style dialog and ::backdrop with Tailwind and you get the best of both.

Example

Try it yourself
Loading editor…
Press Run to see the result.

When to use it

  • A SaaS app opens a confirmation dialog before deleting a workspace, using a fixed backdrop and centered panel built with Tailwind so no CSS file needs to be touched.
  • An image gallery shows a lightbox modal with a close button and keyboard-accessible native <dialog> element, styled with Tailwind utilities for the backdrop and panel.
  • An onboarding flow presents a multi-step wizard inside a modal that blocks the page scroll with overflow-hidden on the body, all toggled from a single data attribute.

More examples

CSS-only backdrop and panel

fixed inset-0 covers the full viewport; bg-black/50 creates the semi-transparent backdrop; flex items-center justify-center centers the panel — all without a single line of custom CSS.

Example · html
<div id="modal" class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/50">
  <div class="bg-white rounded-2xl shadow-xl w-full max-w-md mx-4 p-6">
    <h2 class="text-lg font-bold text-gray-900">Delete workspace?</h2>
    <p class="mt-2 text-sm text-gray-600">This action cannot be undone.</p>
    <div class="mt-6 flex justify-end gap-3">
      <button onclick="document.getElementById('modal').classList.add('hidden')"
              class="px-4 py-2 rounded-lg border border-gray-300 text-sm hover:bg-gray-50">Cancel</button>
      <button class="px-4 py-2 rounded-lg bg-red-600 text-white text-sm font-semibold hover:bg-red-700">Delete</button>
    </div>
  </div>
</div>

Native dialog element styled with Tailwind

The native <dialog> element handles focus trapping and the Escape key for free; backdrop: variant styles its built-in backdrop pseudo-element with Tailwind.

Example · html
<dialog id="my-dialog"
        class="rounded-2xl shadow-xl p-6 w-full max-w-md backdrop:bg-black/50">
  <h2 class="text-lg font-bold text-gray-900">Confirm Action</h2>
  <p class="mt-2 text-sm text-gray-600">Are you sure you want to proceed?</p>
  <div class="mt-6 flex justify-end gap-3">
    <button onclick="document.getElementById('my-dialog').close()"
            class="px-4 py-2 rounded-lg border border-gray-300 text-sm hover:bg-gray-50">Cancel</button>
    <button class="px-4 py-2 rounded-lg bg-cyan-600 text-white text-sm font-semibold hover:bg-cyan-700">Confirm</button>
  </div>
</dialog>
<button onclick="document.getElementById('my-dialog').showModal()"
        class="px-4 py-2 rounded-lg bg-cyan-600 text-white text-sm font-semibold">Open dialog</button>

Animated modal with scroll lock

Adds a fade + scale-in animation via opacity and scale transition utilities, and locks body scroll with overflow-hidden on the body while the modal is open.

Example · html
<div id="modal"
     class="fixed inset-0 z-50 flex items-center justify-center bg-black/50
            opacity-0 pointer-events-none transition-opacity duration-200">
  <div id="modal-panel"
       class="bg-white rounded-2xl shadow-xl w-full max-w-lg mx-4 p-8
              scale-95 transition-transform duration-200">
    <h2 class="text-xl font-bold text-gray-900">Welcome aboard!</h2>
    <p class="mt-3 text-gray-600 text-sm">Complete your profile to get started.</p>
    <button onclick="closeModal()"
            class="mt-6 px-5 py-2 rounded-lg bg-cyan-600 text-white text-sm font-semibold hover:bg-cyan-700">Get started</button>
  </div>
</div>
<script>
  function openModal() {
    document.body.classList.add("overflow-hidden");
    document.getElementById("modal").classList.replace("opacity-0","opacity-100");
    document.getElementById("modal").classList.remove("pointer-events-none");
    document.getElementById("modal-panel").classList.replace("scale-95","scale-100");
  }
  function closeModal() {
    document.body.classList.remove("overflow-hidden");
    document.getElementById("modal").classList.replace("opacity-100","opacity-0");
    document.getElementById("modal").classList.add("pointer-events-none");
    document.getElementById("modal-panel").classList.replace("scale-100","scale-95");
  }
</script>

Discussion

  • Be the first to comment on this lesson.
A Modal / Dialog — Tailwind CSS | SoundsCode