Overlays are the components most often built badly: focus escapes, they cannot animate in, and screen readers never hear them. Modern HTML fixes most of this natively, and Tailwind v4 ships the variants to style it. In this lesson you will build an accessible modal on <dialog>, animate it with starting:, create a dropdown with the Popover API, and add a tooltip that works for mouse and keyboard.
<dialog> opened with showModal() traps focus, closes on Escape, renders in the top layer, and exposes a ::backdrop:
<dialog id="settings" class="m-auto w-full max-w-lg rounded-xl p-6 shadow-xl backdrop:bg-black/50">
<h2 class="text-lg font-semibold">Settings</h2>
<form method="dialog" class="mt-6 flex justify-end">
<button class="rounded-md border px-4 py-2 text-sm">Close</button>
</form>
</dialog>
<button onclick="settings.showModal()">Open</button>m-auto centres the dialog because the browser positions it with inset: 0. backdrop: targets ::backdrop, so backdrop:bg-black/50 dims the page behind. A <form method="dialog"> closes the dialog on submit without JavaScript, and the button's value becomes dialog.returnValue.
A closed dialog is display: none, which normally cannot transition. transition-discrete lets display participate and starting: (@starting-style) sets the state to animate from:
<dialog class="opacity-0 scale-95 transition-all transition-discrete duration-200
open:opacity-100 open:scale-100 starting:open:opacity-0 starting:open:scale-95">Read the classes as a timeline: closed (opacity-0 scale-95), open (open:opacity-100), and the frame the open animation starts from (starting:open:opacity-0); the exit animation follows when open is removed. The backdrop fades the same way with backdrop:opacity-0 open:backdrop:opacity-100 starting:open:backdrop:opacity-0. Add motion-reduce:transition-none for reduced motion.
The popover attribute gives any element top-layer rendering, light dismiss (click outside or Escape) and a toggle button with no JavaScript; Tailwind's open: variant matches :popover-open:
<button popovertarget="user-menu" class="rounded-md border px-3 py-2 text-sm">Account</button>
<div id="user-menu" popover
class="m-0 w-56 rounded-lg border bg-white p-1 shadow-lg
opacity-0 transition-discrete open:opacity-100 starting:open:opacity-0">
<a href="/profile" class="block rounded-md px-3 py-2 text-sm hover:bg-gray-100">Profile</a>
<button class="block w-full rounded-md px-3 py-2 text-left text-sm text-red-600 hover:bg-red-50">Sign out</button>
</div>Because a popover lives in the top layer, absolute positioning relative to the trigger does not apply; the browser centres it. Place it beside its button with CSS anchor positioning where supported or a small positioning library, or use the <details> pattern with group-open: for a menu in normal flow.
A tooltip is a hidden label shown on hover and focus, positioned above its trigger and linked with aria-describedby:
<span class="group relative inline-block">
<button aria-describedby="tip-copy" class="rounded-md px-3 py-2 hover:bg-gray-100">Copy</button>
<span id="tip-copy" role="tooltip"
class="pointer-events-none invisible absolute bottom-full left-1/2 mb-2 -translate-x-1/2
whitespace-nowrap rounded bg-gray-900 px-2 py-1 text-xs text-white opacity-0 transition
group-hover:visible group-hover:opacity-100 group-focus-within:visible group-focus-within:opacity-100">
Copy to clipboard
</span>
</span>bottom-full mb-2 places it above the trigger, left-1/2 -translate-x-1/2 centres it, and pointer-events-none stops flicker when the cursor drifts onto it. Keep tooltips short; anything interactive belongs in a popover.
div without focus trapping, Escape handling or aria-modal; <dialog> provides all three.hidden/block for open and closed states, which cannot animate.Which variant styles the dimmed layer behind an open `<dialog>`?
<dialog> with showModal() for modals; style the overlay with backdrop: and close with <form method="dialog">.transition-discrete, open: and starting:open: classes.popover attribute gives dropdowns light dismiss and top-layer rendering; open: styles the open state.group-hover: and group-focus-within: with role="tooltip" and aria-describedby.Next lesson: Accessibility with Tailwind: Focus, Screen Readers and Motion — make every component usable with a keyboard, a screen reader and reduced-motion settings.