Popovers
A popover is a transient view that appears above other content when people click or tap a control or interactive area.
When to use it
Use a popover for lightweight tools and details that relate to one control. For commands, use a menu. For decisions that block the flow, use a dialog.
Preview
The default placement sits under its control.
Placement top puts the popover above its control.
Positioned by popovers.js. Click outside or press Escape to close.
Anatomy
The surface uses the same overlay glass as alerts: rgba(246, 246, 246, 0.73) over a 20px blur with 180% saturation, so it picks up a little of what sits behind it. The radius is 20px. The shadow is a wide, soft drop (0 8px 48px at 25%). The edge is a crisp 0.5px keyline just outside the surface, plus a bright 1px line on the inside of the top and bottom edges. Content padding is 16px.
The arrow protrudes 10.5px beyond the body edge. Its base spans 35.44px with 45 degree slopes and a rounded tip. It uses a near-opaque fill (rgba(248, 248, 248, 0.95)) that matches the blurred body, so it reads as one surface.
| Class | Role |
|---|---|
.p-popover | The surface. Glass, radius 20, 16px padding, shadow, arrow. |
data-p-popover-place | Placement relative to the trigger: bottom (default), top, left, right. The arrow sits on the opposite edge. |
.p-popover--no-arrow | Removes the arrow for edge-to-edge or detached uses. |
data-p-popover-float | Marks a floating popover. Hidden and fixed until opened by the script. |
Buttons inside a popover are vibrant: their fills blend with the glass instead of sitting on it, with no extra class (see Buttons). Menus work the same way.
Behavior
Include popovers.js and point a trigger at its popover:
<button class="p-button" type="button"
data-p-popover="#account-popover"
aria-expanded="false" aria-haspopup="dialog">
Account
</button>
<div class="p-popover" id="account-popover" data-p-popover-float
data-p-popover-place="bottom" role="dialog" aria-label="Account">
<!-- any content -->
</div>
Clicking the trigger toggles the popover. The script positions it on the placement side, flips it when it would leave the viewport, and keeps an 8px viewport inset. The arrow tracks the center of the trigger. Clicking outside, pressing Escape, scrolling, or resizing closes it. One popover is open at a time.
Opening moves focus to the surface, not into the content. Escape restores focus to the trigger. Give the popover role="dialog" and a label, and keep aria-expanded on the trigger so the state is announced.
States
The popover has two states. Closed: opacity: 0, scale(0.98), no pointer events. Open (.active): full opacity and scale. The transition is 0.18s with the default ease. With prefers-reduced-motion the scale is removed and only the fade remains.
Dark mode
The surface switches to a near-black glass, rgba(26, 26, 26, 0.73), with a matching arrow fill. The keyline darkens to black at 60%, the inner top line drops to white at 23%, and the drop shadow deepens to 45%. Text switches to rgba(255, 255, 255, 0.92). Both the class-based (.p-dark-mode) and automatic (.p-auto-dark-mode) modes are supported.
Accessibility
- With
prefers-reduced-transparencythe glass becomes opaque#F6F6F6(dark:#1E1E1E) and the blur is removed. - Browsers without
backdrop-filterget the same opaque fallback. - Browsers without
clip-path: path()drop the arrow and keep the surface. - Content inside the popover keeps its own focus order. Interactive content should be reachable with Tab after the surface receives focus.