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

~/css/popovers

7 CSS Popovers

3 Pure CSS4 with JSPublished 2026-10-05Free · no sign-up

Seven popovers built on the HTML popover attribute: a basic popover opened by popovertarget, one anchored to its button with CSS anchor positioning, a help popover beside a form label, a list of actions, a delete confirmation, a small rename form and a profile hover card.

01. Native Popover with popovertarget

PopoverHTML

The popover attribute and a button with popovertarget give a popover with light dismiss (click outside), Escape to close and correct focus handling, with no JavaScript. It renders in the top layer, so it is never clipped by overflow: hidden ancestors.

View code
<div class="pop1"><button popovertarget="pop1p">What is a SKU?</button><div id="pop1p" popover><b>SKU</b><p>A stock-keeping unit: your own code for one specific product variant, such as a size and colour.</p></div></div>
.pop1 { font: 14px Inter, system-ui, sans-serif; }
.pop1 button { padding: 9px 14px; border: 1px solid #d0d5dd; border-radius: 8px; background: #fff; font: 600 14px Inter, system-ui, sans-serif; cursor: pointer; }
.pop1 [popover] { width: min(260px, 85vw); margin: auto; padding: 14px; border: 1px solid #e4e7ec; border-radius: 12px; background: #fff; color: #344054; box-shadow: 0 16px 32px -12px rgba(16,24,40,.35); }
.pop1 [popover] b { color: #101828; } .pop1 [popover] p { margin: 4px 0 0; line-height: 1.5; }
.pop1 button:focus-visible { outline: 3px solid #84adff; outline-offset: 2px; }

02. Popover Anchored to Its Button

PopoverAnchor positioning

CSS anchor positioning ties the popover to the button that opens it: position-anchor names the anchor and position-area: bottom places it below, flipping above if there is no room. Browsers without anchor positioning centre the popover instead.

View code
<div class="pop2"><button popovertarget="pop2p" class="trig">Shipping info</button><div id="pop2p" popover><p>Free over $50. Ships in 2 working days from Porto.</p></div></div>
.pop2 { font: 14px Inter, system-ui, sans-serif; }
.pop2 .trig { anchor-name: --pop2; padding: 9px 14px; border: 1px solid #3a4670; border-radius: 8px; background: #1b2238; color: #e2e8f0; font: 600 14px Inter, system-ui, sans-serif; cursor: pointer; }
.pop2 [popover] { width: 220px; margin: 0; padding: 12px; border: 1px solid #334155; border-radius: 12px; background: #0f172a; color: #cbd5e1; }
@supports (position-area: bottom) { .pop2 [popover] { position-anchor: --pop2; position-area: bottom; margin-top: 8px; position-try-fallbacks: flip-block; } }
@supports not (position-area: bottom) { .pop2 [popover] { margin: auto; } }
.pop2 p { margin: 0; line-height: 1.5; }
.pop2 .trig:focus-visible { outline: 2px solid #93c5fd; outline-offset: 2px; }

03. Info Icon Popover

PopoverHelp

A small question-mark button beside a form label opens a help popover. Unlike a hover tooltip it works on touch screens and stays open while the help is read, and the button is named after what it explains: Help with VAT number.

View code
<div class="pop3"><label for="pop3v">VAT number</label><button popovertarget="pop3p" aria-label="Help with VAT number">?</button><input id="pop3v" placeholder="GB123456789"><div id="pop3p" popover><p>Only needed for business orders. You will find it on your VAT registration certificate.</p></div></div>
.pop3 { display: grid; grid-template-columns: 1fr auto; gap: 6px; width: 260px; max-width: 100%; font: 14px Inter, system-ui, sans-serif; }
.pop3 label { align-self: center; color: #101828; font-weight: 700; }
.pop3 button { width: 26px; height: 26px; border: 1px solid #98a2b3; border-radius: 50%; background: #fff; color: #344054; font: 700 13px Inter, system-ui, sans-serif; cursor: pointer; }
.pop3 input { grid-column: 1 / -1; min-width: 0; padding: 10px; border: 1px solid #98a2b3; border-radius: 8px; font: inherit; }
.pop3 [popover] { width: min(240px, 85vw); margin: auto; padding: 12px; border-radius: 10px; border: 0; background: #101828; color: #f2f4f7; }
.pop3 [popover] p { margin: 0; line-height: 1.5; }
.pop3 :focus-visible { outline: 3px solid #84adff; outline-offset: 2px; }

04. Actions Popover

PopoverActions

A More button opens a small list of actions for an item. It is a popover containing ordinary buttons, which keeps keyboard use simple (Tab moves through them), and choosing an action closes the popover with hidePopover().

View code
<div class="pop4"><span>Q3 report.pdf</span><button popovertarget="pop4p" aria-label="More actions for Q3 report">⋯</button><div id="pop4p" popover><button>Rename</button><button>Download</button><button class="del">Delete</button></div><p role="status"></p></div>
.pop4 { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px; width: 280px; max-width: 100%; padding: 10px 12px; box-sizing: border-box; border-radius: 10px; background: #151b2e; color: #e2e8f0; font: 14px Inter, system-ui, sans-serif; }
.pop4 > button { width: 32px; height: 32px; border: 0; border-radius: 8px; background: #1e2640; color: #fff; font-size: 18px; cursor: pointer; }
.pop4 [popover] { display: none; flex-direction: column; min-width: 160px; margin: auto; padding: 6px; border: 1px solid #334155; border-radius: 10px; background: #0f172a; }
.pop4 [popover]:popover-open { display: flex; }
.pop4 [popover] button { padding: 8px 10px; border: 0; border-radius: 6px; background: none; color: #e2e8f0; font: 14px Inter, system-ui, sans-serif; text-align: left; cursor: pointer; }
.pop4 [popover] button:hover { background: #1e2640; } .pop4 .del { color: #fca5a5; }
.pop4 p { flex-basis: 100%; margin: 0; min-height: 1.2em; color: #94a3b8; font-size: 12px; }
.pop4 button:focus-visible { outline: 2px solid #93c5fd; outline-offset: 1px; }
var p = document.getElementById('pop4p'), st = document.querySelector('.pop4 p');
p.querySelectorAll('button').forEach(function (b) { b.onclick = function () { p.hidePopover(); st.textContent = b.textContent + ' chosen (demo)'; }; });

05. Confirm Before Deleting

PopoverConfirm

A small confirmation popover right next to the Delete button, lighter than a full dialog for a low-stakes delete. It holds a question and two buttons, the Cancel button closes it with popovertargetaction="hide", and nothing is deleted until Delete is pressed.

View code
<div class="pop5"><button popovertarget="pop5p" class="del">Delete comment</button><div id="pop5p" popover><p>Delete this comment?</p><div><button popovertarget="pop5p" popovertargetaction="hide">Cancel</button><button class="yes">Delete</button></div></div><p class="st" role="status"></p></div>
.pop5 { font: 14px Inter, system-ui, sans-serif; }
.pop5 .del { padding: 9px 14px; border: 1px solid #fda29b; border-radius: 8px; background: #fff; color: #b42318; font: 600 14px Inter, system-ui, sans-serif; cursor: pointer; }
.pop5 [popover] { margin: auto; padding: 14px; border: 1px solid #e4e7ec; border-radius: 12px; background: #fff; box-shadow: 0 16px 32px -12px rgba(16,24,40,.35); }
.pop5 [popover] p { margin: 0 0 10px; color: #101828; font-weight: 600; }
.pop5 [popover] div { display: flex; gap: 6px; justify-content: flex-end; }
.pop5 [popover] button { padding: 7px 12px; border: 1px solid #d0d5dd; border-radius: 8px; background: #fff; font: 600 13px Inter, system-ui, sans-serif; cursor: pointer; }
.pop5 .yes { border-color: #b42318; background: #b42318; color: #fff; }
.pop5 .st { min-height: 1.2em; margin: 8px 0 0; color: #475467; font-size: 13px; }
.pop5 button:focus-visible { outline: 3px solid #84adff; outline-offset: 2px; }
var p = document.getElementById('pop5p'), st = document.querySelector('.pop5 .st');
p.querySelector('.yes').onclick = function () { p.hidePopover(); st.textContent = 'Comment deleted.'; document.querySelector('.pop5 .del').disabled = true; };

06. Popover with a Small Form

PopoverForm

Rename an item in place with a popover holding a labelled input and Save button. Focus moves into the input when the popover opens, Enter saves and Escape cancels, and the new name updates the item and is announced.

View code
<div class="pop6"><span class="nm">Untitled board</span><button popovertarget="pop6p">Rename</button><form id="pop6p" popover><label for="pop6i">Board name</label><input id="pop6i" value="Untitled board"><button>Save</button></form><p class="st" role="status"></p></div>
.pop6 { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; color: #f1f5f9; font: 14px Inter, system-ui, sans-serif; }
.pop6 .nm { font-weight: 700; }
.pop6 > button { padding: 6px 10px; border: 1px solid #3a4670; border-radius: 8px; background: transparent; color: #c7d2fe; font: 600 13px Inter, system-ui, sans-serif; cursor: pointer; }
.pop6 [popover] { display: none; gap: 6px; width: min(240px, 85vw); margin: auto; padding: 14px; border: 1px solid #334155; border-radius: 12px; background: #0f172a; color: #e2e8f0; }
.pop6 [popover]:popover-open { display: grid; }
.pop6 input { padding: 8px; border: 1px solid #475569; border-radius: 8px; background: #111827; color: #fff; font: inherit; }
.pop6 form button { padding: 8px; border: 0; border-radius: 8px; background: #6366f1; color: #fff; font: 700 13px Inter, system-ui, sans-serif; cursor: pointer; }
.pop6 .st { flex-basis: 100%; margin: 0; min-height: 1.2em; color: #94a3b8; font-size: 12px; }
.pop6 :focus-visible { outline: 2px solid #a5b4fc; outline-offset: 2px; }
var f = document.getElementById('pop6p'), i = document.getElementById('pop6i'), nm = document.querySelector('.pop6 .nm'), st = document.querySelector('.pop6 .st');
f.addEventListener('toggle', function (e) { if (e.newState === 'open') { i.value = nm.textContent; i.select(); } });
f.onsubmit = function (e) { e.preventDefault(); var v = i.value.trim(); if (v) { nm.textContent = v; st.textContent = 'Renamed to ' + v; } f.hidePopover(); };

07. Profile Hover Card

PopoverHover card

Hovering or focusing a username shows a profile card after a short delay, using a manual popover opened with showPopover(). It also opens on keyboard focus, closes on Escape, and the link still goes to the full profile, so the card is a shortcut, never the only way.

View code
<div class="pop7"><p>Commented by <a href="#ana" class="u">@ana</a> 2 hours ago</p><div id="pop7p" popover="manual" role="tooltip"><span class="av" aria-hidden="true">AR</span><b>Ana Ribeiro</b><span>Design lead · 214 posts</span></div></div>
.pop7 { color: #475467; font: 14px Inter, system-ui, sans-serif; } .pop7 p { margin: 0; }
.pop7 .u { color: #6941c6; font-weight: 700; }
.pop7 .u:focus-visible { outline: 3px solid #d6bbfb; outline-offset: 2px; border-radius: 4px; }
#pop7p { display: none; grid-template-columns: auto 1fr; column-gap: 10px; align-items: center; width: 220px; margin: auto; padding: 12px; border: 1px solid #e4e7ec; border-radius: 12px; background: #fff; box-shadow: 0 16px 32px -12px rgba(16,24,40,.35); font: 13px Inter, system-ui, sans-serif; color: #475467; }
#pop7p:popover-open { display: grid; }
#pop7p .av { grid-row: span 2; display: grid; place-items: center; width: 40px; height: 40px; border-radius: 50%; background: #f4ebff; color: #53389e; font-weight: 800; }
#pop7p b { color: #101828; }
var u = document.querySelector('.pop7 .u'), p = document.getElementById('pop7p'), t;
u.setAttribute('aria-describedby', 'pop7p');
function open() { clearTimeout(t); t = setTimeout(function () { if (!p.matches(':popover-open')) p.showPopover(); }, 300); }
function close() { clearTimeout(t); t = setTimeout(function () { if (p.matches(':popover-open')) p.hidePopover(); }, 200); }
u.addEventListener('mouseenter', open); u.addEventListener('focus', open); u.addEventListener('mouseleave', close); u.addEventListener('blur', close);
document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && p.matches(':popover-open')) p.hidePopover(); });

About these CSS Popovers

The popover attribute does the work

Add popover to an element and popovertarget to a button, and the browser handles opening, closing on Escape, closing when clicking outside and showing it in the top layer above everything else.

Anchor it to the trigger

CSS anchor positioning places the popover next to its button: name the button with anchor-name and use position-anchor and position-area on the popover. Add a fallback for browsers without it.

Popover is not a dialog

Popovers are for light, non-blocking content. For something that must be answered before continuing, use a modal <dialog> instead.

Manual popovers for hover cards

popover="manual" turns off light dismiss so script can open and close it on hover and focus with delays. Always open on focus too, and close on Escape.

Frequently asked questions

Which browsers support the popover attribute?

All current major browsers.

Are popovers accessible?

The browser manages focus and dismissal; give the trigger a clear name and keep the content short.

Is a popover the same as a tooltip?

No. Tooltips are short hover hints; popovers can hold content and controls and stay open until dismissed.