Popover
A Tailwind CSS popover component for displaying rich content in a floating panel.
Dimensions
Set the dimensions for the layer.
<button
class="btn"
type="button"
data-sp-toggle="popover"
data-sp-target="#popover-dimensions"
aria-expanded="false"
>
Open Popover
</button>
<div id="popover-dimensions" class="popover">
<div class="mb-3 space-y-1">
<h4 class="text-sm font-medium leading-none">Dimensions</h4>
<p class="text-sm text-muted-foreground">Set the dimensions for the layer.</p>
</div>
<div class="flex flex-col gap-3">
<div class="flex items-center gap-4">
<label class="label w-20 shrink-0">Width</label>
<input class="input" type="text" value="100%" />
</div>
<div class="flex items-center gap-4">
<label class="label w-20 shrink-0">Height</label>
<input class="input" type="text" value="25px" />
</div>
<div class="flex items-center gap-4">
<label class="label w-20 shrink-0">Max. Width</label>
<input class="input" type="text" value="300px" />
</div>
<div class="flex items-center gap-4">
<label class="label w-20 shrink-0">Max. Height</label>
<input class="input" type="text" value="none" />
</div>
</div>
</div>Basic
A simple popover with a title and description.
About this feature
This feature allows you to customize the layout dimensions of any layer in your project. Changes are applied in real time.
<button
class="btn"
type="button"
data-sp-toggle="popover"
data-sp-target="#popover-basic"
aria-expanded="false"
>
Open Popover
</button>
<div id="popover-basic" class="popover">
<div class="mb-3 space-y-1">
<h4 class="text-sm font-medium leading-none">About this feature</h4>
<p class="text-sm text-muted-foreground">
This feature allows you to customize the layout dimensions of any layer
in your project. Changes are applied in real time.
</p>
</div>
</div>Placement
Use data-sp-placement on the trigger to control where the popover appears relative to it (default is bottom).
Bottom Start
Aligned to the start of the trigger.
Bottom
Centered below the trigger.
Bottom End
Aligned to the end of the trigger.
Top Start
Aligned to the start above the trigger.
Top
Centered above the trigger.
Top End
Aligned to the end above the trigger.
Left
Centered to the left of the trigger.
Right
Centered to the right of the trigger.
<button class="btn" type="button" data-sp-toggle="popover" data-sp-target="#popover-bottom-start" data-sp-placement="bottom-start" aria-expanded="false">bottom-start</button>
<div id="popover-bottom-start" class="popover">
<div class="mb-3 space-y-1">
<h4 class="text-sm font-medium leading-none">Bottom Start</h4>
<p class="text-sm text-muted-foreground">Aligned to the start of the trigger.</p>
</div>
</div>
<button class="btn" type="button" data-sp-toggle="popover" data-sp-target="#popover-bottom" data-sp-placement="bottom" aria-expanded="false">bottom</button>
<div id="popover-bottom" class="popover">
<div class="mb-3 space-y-1">
<h4 class="text-sm font-medium leading-none">Bottom</h4>
<p class="text-sm text-muted-foreground">Centered below the trigger.</p>
</div>
</div>
<button class="btn" type="button" data-sp-toggle="popover" data-sp-target="#popover-bottom-end" data-sp-placement="bottom-end" aria-expanded="false">bottom-end</button>
<div id="popover-bottom-end" class="popover">
<div class="mb-3 space-y-1">
<h4 class="text-sm font-medium leading-none">Bottom End</h4>
<p class="text-sm text-muted-foreground">Aligned to the end of the trigger.</p>
</div>
</div>
<button class="btn" type="button" data-sp-toggle="popover" data-sp-target="#popover-top-start" data-sp-placement="top-start" aria-expanded="false">top-start</button>
<div id="popover-top-start" class="popover">
<div class="mb-3 space-y-1">
<h4 class="text-sm font-medium leading-none">Top Start</h4>
<p class="text-sm text-muted-foreground">Aligned to the start above the trigger.</p>
</div>
</div>
<button class="btn" type="button" data-sp-toggle="popover" data-sp-target="#popover-top" data-sp-placement="top" aria-expanded="false">top</button>
<div id="popover-top" class="popover">
<div class="mb-3 space-y-1">
<h4 class="text-sm font-medium leading-none">Top</h4>
<p class="text-sm text-muted-foreground">Centered above the trigger.</p>
</div>
</div>
<button class="btn" type="button" data-sp-toggle="popover" data-sp-target="#popover-top-end" data-sp-placement="top-end" aria-expanded="false">top-end</button>
<div id="popover-top-end" class="popover">
<div class="mb-3 space-y-1">
<h4 class="text-sm font-medium leading-none">Top End</h4>
<p class="text-sm text-muted-foreground">Aligned to the end above the trigger.</p>
</div>
</div>
<button class="btn" type="button" data-sp-toggle="popover" data-sp-target="#popover-left" data-sp-placement="left" aria-expanded="false">left</button>
<div id="popover-left" class="popover">
<div class="mb-3 space-y-1">
<h4 class="text-sm font-medium leading-none">Left</h4>
<p class="text-sm text-muted-foreground">Centered to the left of the trigger.</p>
</div>
</div>
<button class="btn" type="button" data-sp-toggle="popover" data-sp-target="#popover-right" data-sp-placement="right" aria-expanded="false">right</button>
<div id="popover-right" class="popover">
<div class="mb-3 space-y-1">
<h4 class="text-sm font-medium leading-none">Right</h4>
<p class="text-sm text-muted-foreground">Centered to the right of the trigger.</p>
</div>
</div>Offset
Use data-sp-offset on the trigger to control the distance between the trigger and the popover (default is 4).
Flush against the trigger.
12 pixels of breathing room.
<button class="btn" type="button" data-sp-toggle="popover" data-sp-target="#popover-offset-0" data-sp-offset="0" aria-expanded="false">No offset</button>
<div id="popover-offset-0" class="popover">
<p class="text-sm">Flush against the trigger.</p>
</div>
<button class="btn" type="button" data-sp-toggle="popover" data-sp-target="#popover-offset-12" data-sp-offset="12" aria-expanded="false">Large offset</button>
<div id="popover-offset-12" class="popover">
<p class="text-sm">12 pixels of breathing room.</p>
</div>Hover trigger
Add data-sp-trigger="hover" to open the popover on pointer hover. Click still works for touch devices and keyboard users.
Hover-opened popover
Move your pointer here to keep it open. Click the trigger to commit focus into the panel.
<button
class="btn"
type="button"
data-sp-toggle="popover"
data-sp-target="#popover-hover"
data-sp-trigger="hover"
aria-expanded="false"
>
Hover me
</button>
<div id="popover-hover" class="popover">
<div class="space-y-1">
<h4 class="text-sm font-medium leading-none">Hover-opened popover</h4>
<p class="text-sm text-muted-foreground">
Move your pointer here to keep it open. Click the trigger to commit
focus into the panel.
</p>
</div>
</div>How it works
The popover component uses a small JavaScript module that handles opening, closing, positioning, and focus trapping.
Structure
A popover consists of two elements linked by id:
[data-sp-toggle="popover"]withdata-sp-target="#id"— the trigger button.popoverwith a matchingid— the floating content panel
<button data-sp-toggle="popover" data-sp-target="#my-popover" aria-expanded="false">
Open Popover
</button>
<div id="my-popover" class="popover">
<!-- Any content -->
</div>The trigger and the popover can live anywhere in the DOM as long as the id matches.
Opening and closing
Add data-sp-toggle="popover" and data-sp-target to the trigger to toggle the linked popover on click. Clicking outside the popover or pressing Escape closes it. When opened, focus moves into the popover.
For programmatic control, use the global sp.popover module:
const trigger = document.querySelector("[data-sp-target='#my-popover']");
const popover = document.getElementById("my-popover");
sp.popover.open(trigger);
sp.popover.close(popover);
sp.popover.toggle(trigger);Focus trapping
When the popover is open, Tab and Shift+Tab cycle focus through the focusable elements inside. Focus wraps from the last element back to the first, and vice versa. This keeps keyboard users within the popover until they explicitly close it with Escape.
Animation
The popover includes a default fade and zoom animation. When opened, the JavaScript sets data-state="open" on the popover element.
To customize animations, add no-animation to disable the defaults and use your own classes with data-[state=open]: selectors:
<div
id="my-popover"
class="popover no-animation data-[state=open]:animate-in data-[state=open]:slide-in-from-top-2"
>
<!-- Custom slide animation -->
</div>Default fade and zoom animation.
Animations are disabled.
<button class="btn" type="button" data-sp-toggle="popover" data-sp-target="#popover-anim-default" aria-expanded="false">Default</button>
<div id="popover-anim-default" class="popover">
<p class="text-sm">Default fade and zoom animation.</p>
</div>
<button class="btn" type="button" data-sp-toggle="popover" data-sp-target="#popover-anim-none" aria-expanded="false">No Animation</button>
<div id="popover-anim-none" class="popover no-animation">
<p class="text-sm">Animations are disabled.</p>
</div>
<button class="btn" type="button" data-sp-toggle="popover" data-sp-target="#popover-anim-slide" aria-expanded="false">Slide from Top</button>
<div id="popover-anim-slide" class="popover no-animation data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-top-2">
<p class="text-sm">Slides in from the top.</p>
</div>The default animations use tw-animate-css utilities. You can customize or replace these with your own animations.
Accessibility
The popover JavaScript module provides focus trapping and proper ARIA attributes.
For proper accessibility, add aria-expanded="false" to the trigger button:
<button data-sp-toggle="popover" data-sp-target="#my-popover" aria-expanded="false">
Open
</button>The JavaScript will automatically update aria-expanded when the popover opens and closes.
Keyboard navigation
| Key | Action |
|---|---|
Enter / Space | Opens/closes popover when trigger is focused |
Escape | Closes the popover and returns focus to the trigger |
Tab | Cycles focus through focusable elements within the popover |
Shift + Tab | Cycles focus backward through focusable elements within the popover |
Class reference
All available classes for the popover component.
| Class | Description |
|---|---|
popover | The floating content panel |
no-animation | Disables default animation on the popover |
<button data-sp-toggle="popover" data-sp-target="#my-popover">Open</button>
<div id="my-popover" class="popover">
<!-- Any content -->
</div>Data attributes
All data attributes for the popover component.
| Attribute | Element | Description |
|---|---|---|
data-sp-toggle="popover" | Button | Marks the trigger |
data-sp-target="#id" | Button | The id of the linked .popover |
data-sp-trigger | Button | "click" (default) or "hover" |
data-sp-placement | Button | Popover position (default: bottom) |
data-sp-offset | Button | Distance from trigger in pixels (default: 4) |
aria-expanded | Button | Set "false" initially; JS toggles on open/close |
data-state | .popover | Set to open when popover is visible |
<button
data-sp-toggle="popover"
data-sp-target="#my-popover"
data-sp-placement="bottom-start"
data-sp-offset="8"
aria-expanded="false"
>
Open
</button>
<div id="my-popover" class="popover">
<!-- Any content -->
</div>