Popper
Headless floating UI primitive — a trigger and a teleported, positioned content panel. Aurora's Tooltip, Menu, and DatePicker are all built on it. Use it directly when you need a custom popover and the higher-level wrappers don't fit.
The three pieces:
Popper— root; manages open state and positioning via@popperjs/corePopperTrigger— the element users click or hover to openPopperContent— the floating body; teleports to<body>so it isn't trapped by overflow
Basic — click
html
<Popper>
<PopperTrigger>
<Button>Click me</Button>
</PopperTrigger>
<PopperContent>
<div class="bg-white p-4 rounded-2 shadow-300 border border-primary">
<p>This is the popper content.</p>
</div>
</PopperContent>
</Popper>PopperContent is unstyled — wrap it with whatever surface (bg-white, shadow, rounded) the design calls for.
Hover trigger
html
<Popper trigger="hover">
<PopperTrigger>
<Button>Hover me</Button>
</PopperTrigger>
<PopperContent>…</PopperContent>
</Popper>For tooltips specifically, prefer <TextTooltip> / <RichTextTooltip> — they wrap this same primitive with the standard tooltip surface.
Placement
html
<Popper placement="top">…</Popper>
<Popper placement="bottom">…</Popper>placement accepts any value from @popperjs/core's Placement type:
top · top-start · top-end · bottom · bottom-start · bottom-end · left · left-start · left-end · right · right-start · right-end
By default, the popper flips to the opposite side when there isn't room. Disable with :allow-flip="false".
Controlled open state
isOpen is two-way bindable. Use it when something outside PopperTrigger should open or close the popper.
html
<Popper v-model:is-open="isOpen">
<PopperTrigger>
<Button>Toggle</Button>
</PopperTrigger>
<PopperContent>
<div class="bg-white p-4 rounded-2 shadow-300 border border-primary">
<Button @click="isOpen = false">Close from inside</Button>
</div>
</PopperContent>
</Popper>The PopperTrigger and PopperContent slots also expose open, close, and isOpen props if you'd rather drive things from inside the slot:
html
<PopperContent v-slot="{ close }">
<Button @click="close">Done</Button>
</PopperContent>Static (no outside-click dismiss)
By default, clicking outside the popper closes it. Pass static to keep it open until you call close explicitly — useful for confirmation flows.
html
<Popper static v-model:is-open="isOpen">…</Popper>ESC still closes a static popper.
Offset and animation
html
<Popper :offset="[0, 8]" animate>…</Popper>offsetis[skid, distance]—skidshifts along the trigger's main axis,distancepushes the content away from the trigger.animatetoggles a small fade + scale on open/close (100ms).
When to reach for Popper
- You're building a custom popover that doesn't fit the
TooltiporMenushape - You need a click-to-open searchable dropdown — see
Listboxinside aPopper
When not to use Popper
Popper — Props
| Prop | Type | Default | Description |
|---|---|---|---|
isOpen | boolean | false | Two-way bound open state. Use v-model:is-open. |
placement | Placement | 'bottom-end' | Where the content sits relative to the trigger. See list above. |
trigger | 'click' | 'hover' | 'click' | How the popper opens. |
offset | [number, number] | [0, 0] | [skid, distance] from the trigger. |
allowFlip | boolean | true | Flip to the opposite side when there isn't room on the preferred side. |
static | boolean | false | Don't close when the user clicks outside. |
zIndex | number | 50000 | Stacking context for the teleported content. |
openDelay | number | 0 | Milliseconds before opening (handy for hover triggers). |
closeDelay | number | 0 | Milliseconds before closing. |
animate | boolean | false | Fade + scale the content on open/close. |
disabled | boolean | false | Trigger does nothing; popper can't open. |
PopperTrigger — Slot
| Slot | Slot props | Description |
|---|---|---|
default | { isOpen, open, close, disabled } | The element users click or hover. |
PopperContent — Slot
| Slot | Slot props | Description |
|---|---|---|
default | { isOpen, open, close } | The floating body. Teleports to <body>. |
Notes
- Content is teleported to
<body>, so it escapes parentoverflow: hiddenand stacking contexts. - Pressing Escape while the popper is open closes it.
- Aurora's
Popperuses@popperjs/coreunder the hood — everyPlacementvalue supported there works here.