SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/tailwind-popovers

6 Tailwind Popovers

2 Tailwind4 with JSPublished 2026-10-05Free · no sign-up

Six popovers styled with Tailwind: a share panel on the native popover attribute, a filter panel anchored under its button, a profile hover card, an inline delete confirmation, a three-dot actions menu and a click-to-show info bubble.

01. Tailwind Native Popover

TailwindPopoverPopover API

A share panel built on the HTML popover attribute: popovertarget on the button opens it with no script, clicking outside or pressing Escape closes it, and it sits in the top layer above everything. Tailwind styles it like any element.

View code
<button type="button" popovertarget="twpo1" class="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-semibold text-white hover:bg-indigo-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Share</button>
<div id="twpo1" popover class="w-64 max-w-[calc(100vw-32px)] rounded-2xl bg-white p-4 text-slate-900 shadow-xl ring-1 ring-slate-200 backdrop:bg-slate-900/20">
  <p class="font-bold">Share this page</p>
  <div class="mt-3 grid gap-1 text-sm"><a href="#email" class="rounded-lg px-3 py-2 hover:bg-slate-100">Send by email</a><a href="#social" class="rounded-lg px-3 py-2 hover:bg-slate-100">Post to social</a></div>
  <button type="button" popovertarget="twpo1" popovertargetaction="hide" class="mt-2 w-full rounded-lg bg-slate-100 py-2 text-sm font-semibold text-slate-800 hover:bg-slate-200 focus-visible:outline focus-visible:outline-2 focus-visible:outline-indigo-600">Close</button>
</div>

02. Tailwind Anchored Filter Popover

TailwindPopoverDropdown

A filter panel that opens directly under its button. The script handles aria-expanded, closes the panel on Escape or an outside click, and returns focus to the button, the details that make a popover feel right.

View code
<div class="relative inline-block">
  <button id="twpo2b" type="button" aria-expanded="false" aria-controls="twpo2" class="inline-flex items-center gap-1 rounded-lg bg-white px-4 py-2 text-sm font-semibold text-slate-800 ring-1 ring-slate-300 hover:bg-slate-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-indigo-600">Filters <span aria-hidden="true">▾</span></button>
  <div id="twpo2" hidden class="absolute left-1/2 top-full z-10 mt-2 w-56 -translate-x-1/2 rounded-xl bg-white p-3 text-sm shadow-lg ring-1 ring-slate-200">
    <fieldset><legend class="font-semibold text-slate-900">Show</legend>
      <label class="mt-2 flex items-center gap-2 text-slate-700"><input type="checkbox" class="size-4 accent-indigo-600" checked> In stock</label>
      <label class="mt-1 flex items-center gap-2 text-slate-700"><input type="checkbox" class="size-4 accent-indigo-600"> On sale</label>
    </fieldset>
    <button type="button" data-apply class="mt-3 w-full rounded-lg bg-indigo-600 py-1.5 font-semibold text-white hover:bg-indigo-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Apply</button>
  </div>
</div>
var b = document.getElementById('twpo2b'), p = document.getElementById('twpo2');
function set(open, refocus) { b.setAttribute('aria-expanded', open); p.hidden = !open; if (!open && refocus) b.focus(); }
b.onclick = function () { set(p.hidden); };
p.querySelector('[data-apply]').onclick = function () { set(false, true); };
document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && !p.hidden) set(false, true); });
document.addEventListener('click', function (e) { if (!p.hidden && !p.contains(e.target) && e.target !== b && !b.contains(e.target)) set(false); });

03. Tailwind Profile Hover Card

TailwindPopoverHover

Hovering or focusing a name shows a small profile card above it after a short delay. The card holds information only, no buttons, and it is linked to the name with aria-describedby so screen reader users get the same details.

View code
<p class="max-w-xs text-sm text-slate-700">Photo by <span class="group relative inline-block"><a href="#maya" aria-describedby="twpo3" class="font-semibold text-indigo-700 underline underline-offset-2 focus-visible:outline focus-visible:outline-2 focus-visible:outline-indigo-600">Maya Chen</a>
  <span id="twpo3" class="invisible absolute bottom-full left-1/2 z-10 mb-2 w-56 -translate-x-1/2 rounded-xl bg-white p-3 text-left opacity-0 shadow-xl ring-1 ring-slate-200 transition delay-150 duration-150 group-hover:visible group-hover:opacity-100 group-focus-within:visible group-focus-within:opacity-100"><span class="flex items-center gap-2"><span aria-hidden="true" class="grid size-9 place-items-center rounded-full bg-rose-600 text-xs font-bold text-white">MC</span><span><b class="block text-slate-900">Maya Chen</b><span class="text-xs text-slate-600">Food photographer, Lisbon</span></span></span><span class="mt-2 block text-xs text-slate-600">120 photos · joined 2021</span></span>
</span>, shot on a rainy morning at the market.</p>

04. Tailwind Inline Delete Confirmation

TailwindPopoverConfirm

A small confirmation bubble that appears above a Delete button instead of a full modal. Focus moves to Cancel, Escape backs out, and the result is announced in a status message.

View code
<div class="flex w-full max-w-xs items-center justify-between rounded-xl bg-white p-3 shadow-sm ring-1 ring-slate-200">
  <p id="twpo4f" class="text-sm font-medium text-slate-800">invoice-0912.pdf</p>
  <div class="relative">
    <button id="twpo4b" type="button" aria-expanded="false" aria-controls="twpo4" class="rounded-lg px-3 py-1.5 text-sm font-semibold text-red-700 hover:bg-red-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-red-600">Delete</button>
    <div id="twpo4" hidden role="dialog" aria-labelledby="twpo4t" class="absolute bottom-full right-0 z-10 mb-2 w-48 rounded-xl bg-white p-3 shadow-xl ring-1 ring-slate-200">
      <p id="twpo4t" class="text-sm font-semibold text-slate-900">Delete this file?</p>
      <div class="mt-2 flex justify-end gap-2"><button type="button" data-no class="rounded-md px-2.5 py-1 text-sm text-slate-700 hover:bg-slate-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-indigo-600">Cancel</button><button type="button" data-yes class="rounded-md bg-red-600 px-2.5 py-1 text-sm font-semibold text-white hover:bg-red-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600">Delete</button></div>
    </div>
  </div>
</div>
<p id="twpo4s" role="status" class="sr-only"></p>
var b = document.getElementById('twpo4b'), p = document.getElementById('twpo4');
function close() { p.hidden = true; b.setAttribute('aria-expanded', 'false'); b.focus(); }
b.onclick = function () { p.hidden = false; b.setAttribute('aria-expanded', 'true'); p.querySelector('[data-no]').focus(); };
p.querySelector('[data-no]').onclick = close;
p.addEventListener('keydown', function (e) { if (e.key === 'Escape') close(); });
p.querySelector('[data-yes]').onclick = function () { p.hidden = true; document.getElementById('twpo4f').textContent = 'File deleted'; b.disabled = true; b.classList.add('opacity-50'); document.getElementById('twpo4s').textContent = 'invoice-0912.pdf deleted'; };

05. Tailwind Actions Menu Popover

TailwindPopoverMenu

A three-dot button that opens a list of actions in a native popover, positioned under the button by a few lines of script on the toggle event. Light dismiss and Escape come free with the popover attribute.

View code
<div class="flex w-full max-w-xs items-center justify-between rounded-xl bg-slate-800 p-3 text-slate-100">
  <p class="text-sm font-semibold">Q3 report.xlsx</p>
  <button id="twpo5b" type="button" popovertarget="twpo5" aria-label="More actions for Q3 report" class="grid size-8 place-items-center rounded-lg text-lg hover:bg-white/10 focus-visible:outline focus-visible:outline-2 focus-visible:outline-sky-300">⋯</button>
</div>
<div id="twpo5" popover class="m-0 w-44 rounded-xl bg-slate-900 p-1 text-sm text-slate-100 shadow-2xl ring-1 ring-white/10">
  <button type="button" class="block w-full rounded-lg px-3 py-2 text-left hover:bg-white/10 focus-visible:bg-white/10 focus-visible:outline-none">Rename</button>
  <button type="button" class="block w-full rounded-lg px-3 py-2 text-left hover:bg-white/10 focus-visible:bg-white/10 focus-visible:outline-none">Duplicate</button>
  <button type="button" class="block w-full rounded-lg px-3 py-2 text-left text-red-300 hover:bg-white/10 focus-visible:bg-white/10 focus-visible:outline-none">Delete</button>
</div>
var b = document.getElementById('twpo5b'), p = document.getElementById('twpo5');
p.addEventListener('toggle', function (e) { if (e.newState !== 'open') return; var r = b.getBoundingClientRect(); p.style.inset = 'auto'; p.style.top = r.bottom + 6 + 'px'; p.style.left = Math.max(8, Math.min(r.right - p.offsetWidth, innerWidth - p.offsetWidth - 8)) + 'px'; p.querySelector('button').focus(); });
p.querySelectorAll('button').forEach(function (x) { x.onclick = function () { p.hidePopover(); b.focus(); }; });

06. Tailwind Info Toggletip

TailwindPopoverTooltip

An i button that shows a short explanation in a bubble when clicked, the toggletip pattern. The bubble's text is written into a status region on demand, so screen readers announce it at the moment it appears.

View code
<p class="flex items-center gap-1.5 text-sm font-semibold text-slate-800">Delivery fee: $4
  <span class="relative inline-flex">
    <button id="twpo6b" type="button" aria-label="About the delivery fee" class="grid size-5 place-items-center rounded-full bg-slate-200 text-xs font-bold text-slate-700 hover:bg-slate-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-indigo-600">i</button>
    <span id="twpo6" role="status" class="absolute bottom-full left-1/2 mb-2 w-52 -translate-x-1/2"></span>
  </span>
</p>
var b = document.getElementById('twpo6b'), s = document.getElementById('twpo6');
function close() { s.innerHTML = ''; }
b.onclick = function () { if (s.innerHTML) { close(); return; } s.innerHTML = '<span class="block rounded-lg bg-slate-900 px-3 py-2 text-xs font-normal text-white shadow-lg">Free on orders over $50. Otherwise a flat $4 anywhere in the country.</span>'; };
document.addEventListener('keydown', function (e) { if (e.key === 'Escape') close(); });
document.addEventListener('click', function (e) { if (e.target !== b) close(); });

About these Tailwind Popovers

Try the popover attribute first

popover plus popovertarget gives open, close, Escape and outside-click dismissal with no script, and puts the panel in the top layer.

Anchor with a little script

Native popovers open in the centre; to place one under its button, set top and left from the button's position on the toggle event.

Manage focus

When a popover holds actions, move focus into it on open and back to the trigger on close, as the confirmation and menu demos do.

Hover cards hold information only

Content that appears on hover should not contain buttons; connect it with aria-describedby so it is available without a mouse.

Frequently asked questions

Is the popover attribute supported?

Yes, in all current major browsers.

Tooltip or toggletip?

Tooltips label controls on hover; toggletips show extra information on click and announce it.

Is there a CSS version?

Yes, see the CSS popovers collection.