Popover
The popover floats a panel next to its trigger for rich content like forms and settings, and closes when the user clicks away. For a simple text hint, reach for the tooltip; for a menu of actions, use the dropdown.
<button id="popover-demo-trigger" class="btn btn-outline">Open popover</button>
<div class="popover w-80" data-sp-toggle="#popover-demo-trigger">
<div class="popover-header">
<h4 class="popover-title">Dimensions</h4>
<p class="popover-description">Set the dimensions for the layer.</p>
</div>
<div class="grid gap-2">
<div class="grid grid-cols-3 items-center gap-4">
<label class="label" for="popover-width">Width</label>
<input class="input col-span-2 h-8" id="popover-width" value="100%" />
</div>
<div class="grid grid-cols-3 items-center gap-4">
<label class="label" for="popover-max-width">Max. width</label>
<input
class="input col-span-2 h-8"
id="popover-max-width"
value="300px"
/>
</div>
<div class="grid grid-cols-3 items-center gap-4">
<label class="label" for="popover-height">Height</label>
<input class="input col-span-2 h-8" id="popover-height" value="25px" />
</div>
<div class="grid grid-cols-3 items-center gap-4">
<label class="label" for="popover-max-height">Max. height</label>
<input
class="input col-span-2 h-8"
id="popover-max-height"
value="none"
/>
</div>
</div>
</div>Usage
Add the popover class to the panel and point it at its trigger with data-sp-toggle. It opens on click, positions itself relative to the trigger, and closes on an outside click or Escape.
<!-- This is the trigger -->
<button id="my-trigger" class="btn btn-outline">Open Popover</button>
<!-- This is the popover -->
<div class="popover" data-sp-toggle="#my-trigger">Popover content</div>Examples
Basic
A simple popover with a header, title, and description.
Placement
Set where the panel opens relative to its trigger with data-sp-placement. It accepts any Floating UI placement, like top, right, or bottom-end, and the panel flips to the opposite side on its own when there isn't room.
<div class="flex gap-6">
<button id="popover-start-trigger" class="btn btn-outline btn-sm">
Start
</button>
<button id="popover-center-trigger" class="btn btn-outline btn-sm">
Center
</button>
<button id="popover-end-trigger" class="btn btn-outline btn-sm">End</button>
</div>
<div
class="popover w-40"
data-sp-toggle="#popover-start-trigger"
data-sp-placement="bottom-start"
>
Aligned to start
</div>
<div
class="popover w-40"
data-sp-toggle="#popover-center-trigger"
data-sp-placement="bottom"
>
Aligned to center
</div>
<div
class="popover w-40"
data-sp-toggle="#popover-end-trigger"
data-sp-placement="bottom-end"
>
Aligned to end
</div>With form
A popover holding a compact form, laid out with horizontal fields.
<button id="popover-form-trigger" class="btn btn-outline">Open Popover</button>
<div class="popover w-64" data-sp-toggle="#popover-form-trigger">
<div class="popover-header">
<h4 class="popover-title">Dimensions</h4>
<p class="popover-description">Set the dimensions for the layer.</p>
</div>
<div class="field-group gap-4">
<div class="field field-horizontal">
<label class="label w-1/2" for="popover-form-width">Width</label>
<input class="input" id="popover-form-width" value="100%" />
</div>
<div class="field field-horizontal">
<label class="label w-1/2" for="popover-form-height">Height</label>
<input class="input" id="popover-form-height" value="25px" />
</div>
</div>
</div>Hover
Add data-sp-mode="hover" to open on hover instead of click.
<button id="popover-hover-trigger" class="btn btn-outline">Hover me</button>
<div
class="popover"
data-sp-toggle="#popover-hover-trigger"
data-sp-mode="hover"
>
<div class="popover-header">
<h4 class="popover-title">Quick preview</h4>
<p class="popover-description">This popover opens on hover.</p>
</div>
</div>RTL
Add dir="rtl" to any parent element, typically <html>, to render right-to-left. See the RTL guide for details.
<div id="popover-rtl-demo" dir="rtl" class="flex flex-wrap justify-center gap-2">
<button id="popover-rtl-left-trigger" class="btn btn-outline" data-i18n="left">يسار</button>
<button id="popover-rtl-top-trigger" class="btn btn-outline" data-i18n="top">أعلى</button>
<button id="popover-rtl-bottom-trigger" class="btn btn-outline" data-i18n="bottom">أسفل</button>
<button id="popover-rtl-right-trigger" class="btn btn-outline" data-i18n="right">يمين</button>
<div class="popover" data-sp-toggle="#popover-rtl-left-trigger" data-sp-placement="left">
<div class="popover-header">
<h4 class="popover-title" data-i18n="title">الأبعاد</h4>
<p class="popover-description" data-i18n="description">تعيين الأبعاد للطبقة.</p>
</div>
</div>
<div class="popover" data-sp-toggle="#popover-rtl-top-trigger" data-sp-placement="top">
<div class="popover-header">
<h4 class="popover-title" data-i18n="title">الأبعاد</h4>
<p class="popover-description" data-i18n="description">تعيين الأبعاد للطبقة.</p>
</div>
</div>
<div class="popover" data-sp-toggle="#popover-rtl-bottom-trigger" data-sp-placement="bottom">
<div class="popover-header">
<h4 class="popover-title" data-i18n="title">الأبعاد</h4>
<p class="popover-description" data-i18n="description">تعيين الأبعاد للطبقة.</p>
</div>
</div>
<div class="popover" data-sp-toggle="#popover-rtl-right-trigger" data-sp-placement="right">
<div class="popover-header">
<h4 class="popover-title" data-i18n="title">الأبعاد</h4>
<p class="popover-description" data-i18n="description">تعيين الأبعاد للطبقة.</p>
</div>
</div>
</div>
<script>
const translations = {
ar: { dir: "rtl", title: "الأبعاد", description: "تعيين الأبعاد للطبقة.", left: "يسار", top: "أعلى", bottom: "أسفل", right: "يمين" },
he: { dir: "rtl", title: "מימדים", description: "הגדר את המימדים לשכבה.", left: "שמאל", top: "למעלה", bottom: "למטה", right: "ימין" },
en: { dir: "ltr", title: "Dimensions", description: "Set the dimensions for the layer.", left: "Left", top: "Top", bottom: "Bottom", right: "Right" },
};
window.addEventListener("message", (e) => {
const t = translations[e.data?.lang];
if (e.data?.type !== "sp-language" || !t) return;
document.getElementById("popover-rtl-demo").dir = t.dir;
document.querySelectorAll("[data-i18n]").forEach((el) => {
el.textContent = t[el.dataset.i18n];
});
});
</script>Options
Popover
Set each option with its own data-sp-* attribute on the popover element.
<div
class="popover"
data-sp-toggle="#my-trigger"
data-sp-placement="bottom-start"
></div>With data-sp-static the popover skips anchored positioning entirely and you position it yourself with utility classes. The toggle wiring, dismissal, and top layer behavior stay. Use it for panels that are viewport-relative instead of trigger-relative, like a full-screen mobile menu:
<div
class="popover inset-x-0 top-14 bottom-0 h-auto w-auto rounded-none"
data-sp-toggle="#menu-trigger"
data-sp-static
>
...
</div>JavaScript
Events
Lifecycle events fire on the popover element; before* events are cancelable.
el.addEventListener("sp-beforeshow", (e) => {
if (!ready) e.preventDefault(); // veto the open
});Methods
Get the instance with sp.popover(el), then call its methods:
const popover = sp.popover(document.querySelector("#my-popover"));
popover.show();