Examples
Default
Default popover surface with prose padding and shadow-lg elevation.
With header
Quick filters
Header takes the popover's full width via negative margins keyed to scoped padding.
Size
Small (14rem).
Default (20rem).
Large (32rem) for richer content.
no-border
Borderless surface carried by shadow alone; pair with a plain prose block or an image.
Basic usage
A floating surface anchored to its trigger. Click outside or press Escape to close.
<div>
<button
class="button"
type="button"
commandfor="pop-basic"
command="toggle-popover"
>
Open popover
</button>
<div class="popover" id="pop-basic" popover>
<p>
A floating surface anchored to its trigger. Click outside or press
Escape to close.
</p>
</div>
</div>
Header (<header>)
Quick tips
The header's bottom border extends to the popover's edges by pulling through the surface padding.
<div>
<button
class="button"
type="button"
commandfor="pop-header"
command="toggle-popover"
>
Help
</button>
<div class="popover" id="pop-header" popover>
<header>
<h3>Quick tips</h3>
<button
class="dismiss"
type="button"
commandfor="pop-header"
command="hide-popover"
aria-label="Close"
>
×
</button>
</header>
<p>
The header's bottom border extends to the popover's edges by pulling
through the surface padding.
</p>
</div>
</div>
Size
Compact bubble for short hints.
The default size accommodates a paragraph or two of explanatory content without feeling cramped.
A larger surface for content that needs more room -- a small form, a longer explanation, or a list of grouped options that wouldn't fit in the default size.
No max-width. Useful when the surface is meant to span much of the viewport, mega-menu style. Pair with composition primitives for internal layout.
<div class="cluster is-tight">
<button
class="button is-outlined"
type="button"
commandfor="pop-sm"
command="toggle-popover"
>
Small
</button>
<button
class="button is-outlined"
type="button"
commandfor="pop-default"
command="toggle-popover"
>
Default
</button>
<button
class="button is-outlined"
type="button"
commandfor="pop-lg"
command="toggle-popover"
>
Large
</button>
<button
class="button is-outlined"
type="button"
commandfor="pop-wide"
command="toggle-popover"
>
Wide
</button>
<div class="popover is-sm" id="pop-sm" popover>
<p>Compact bubble for short hints.</p>
</div>
<div class="popover" id="pop-default" popover>
<p>
The default size accommodates a paragraph or two of explanatory content
without feeling cramped.
</p>
</div>
<div class="popover is-lg" id="pop-lg" popover>
<p>
A larger surface for content that needs more room -- a small form, a
longer explanation, or a list of grouped options that wouldn't fit in
the default size.
</p>
</div>
<div class="popover is-wide" id="pop-wide" popover>
<p>
No max-width. Useful when the surface is meant to span much of the
viewport, mega-menu style. Pair with composition primitives for internal
layout.
</p>
</div>
</div>
Placement
Default placement: below the trigger.
Above the trigger.
To the inline-start side of the trigger.
To the inline-end side of the trigger.
<div class="cluster is-tight">
<button
class="button is-outlined"
type="button"
commandfor="pop-bottom"
command="toggle-popover"
>
Below
</button>
<button
class="button is-outlined"
type="button"
commandfor="pop-top"
command="toggle-popover"
>
Above
</button>
<button
class="button is-outlined"
type="button"
commandfor="pop-start"
command="toggle-popover"
>
Start
</button>
<button
class="button is-outlined"
type="button"
commandfor="pop-end"
command="toggle-popover"
>
End
</button>
<div class="popover" id="pop-bottom" popover>
<p>Default placement: below the trigger.</p>
</div>
<div class="popover is-top" id="pop-top" popover>
<p>Above the trigger.</p>
</div>
<div class="popover is-start" id="pop-start" popover>
<p>To the inline-start side of the trigger.</p>
</div>
<div class="popover is-end" id="pop-end" popover>
<p>To the inline-end side of the trigger.</p>
</div>
</div>
Source comments
Popover -- floating content surface anchored to a trigger via the Popover API
Structure -- semantic HTML carries the regions, no slot classes:
<button class="button" type="button"
commandfor="my-popover" command="toggle-popover">Open</button>
<div class="popover" id="my-popover" popover>
<header> <-- optional first-child header slot;
<h3>Title</h3> full-bleed bordered separator
</header>
<p>Body content sits directly inside the popover.</p>
</div>
Header slot (<header> as first child)
A <header> placed as the first child of the popover receives a
full-bleed bottom border treatment: negative inline margins equal to
the popover's inline padding pull the separator flush with the
popover's edges (P2 test 2). <header> at any other position stays at
UA defaults; the recipe is keyed to first-child only.
Heading level (h2-h6) is the author's choice per document hierarchy;
the header normalizes visual size via --font-size-base.
Toggle: native via commandfor + command="toggle-popover"
Close: native via command="hide-popover" (from inside or outside the
popover), or light dismiss (click outside / Escape)
Note: brio uses commandfor + command across every overlay (dialog,
popover, dropdown). The native popovertarget attribute also invokes
popovers, but combining popovertarget with command on the same trigger
fires two invocations per click and produces stuck-state bugs across
browsers. Pick one mechanism per trigger; brio picks commandfor.
Archetype: Overlay surface (P3). Generic floating surface; no companion
custom element. Focus management is the consumer's responsibility because
what to focus depends on the popover's content. For menu-shaped patterns
with focus + keyboard wiring, see dropdown.
Positioning: CSS anchor positioning. The Popover API provides an implicit
anchor (the trigger button that invoked the popover), so no anchor-name
declaration is required. position-try-fallbacks flips the panel on
overflow. Browsers without anchor positioning render the popover at the
browser-default top-layer position -- functional, just not anchored.
Animation: @starting-style + transition-behavior: allow-discrete on the
transform axis at top level. Opacity transition is fenced behind a non-
Safari @supports block to avoid a known class of Safari quirks where
opacity transitions on top-layer popovers produce visible jank. See the
dedicated comment block below for details.
No <footer> recipe (intentional divergence from dialog). A footer slot
would only earn P2 if a variant remapped it, and popover doesn't have
such variants. Consumers compose with .cluster, .stack, etc. when they
need footer-like trailing layouts.
Reference
Source: src/css/popover.css
Applies to
.popover
Classes
| Class | Kind | Description |
|---|---|---|
.is-top | variant | Anchored above the trigger |
.is-start | variant | Anchored to the inline-start side of the trigger |
.is-end | variant | Anchored to the inline-end side of the trigger |
.is-sm | variant | Narrow popover at 14rem max width |
.is-lg | variant | Wide popover at 32rem max width |
.is-wide | variant | Uncapped width |
.no-border | opt-out | Removes the border |
Slots
| Slot | Description |
|---|---|
> header:first-child | Header slot -- full-bleed bordered separator (P2 test 2: extends through parent's --popover-padding-inline via negative inline margins). Keyed to first-child so a <header> dropped mid-popover stays at UA defaults instead of breaking layout. |
Tokens
| Token | Default | Description |
|---|---|---|
--popover-bg | var(--color-surface-float) | Background color for the popover surface |
--popover-color | var(--color-text) | Text color inside the popover |
--popover-border-color | var(--color-border) | Border color for the popover surface |
--popover-border-width | var(--border-width-default) | Border width for the popover surface |
--popover-header-border-color | var(--color-border) | Separator border color below a first-child header |
--popover-radius | var(--radius-md) | Border radius for the popover |
--popover-padding-inline | var(--ui-pad-inline-snug) | Inline-axis padding inside the popover |
--popover-padding-block | var(--ui-pad-block-snug) | Block-axis padding inside the popover |
--popover-box-shadow | var(--shadow-lg) | Box shadow for the popover |
--popover-max-width | 20rem | Maximum width of the popover |
--popover-offset | var(--space-3xs) | Gap between the trigger and the popover edge |