UIPopover
places its content in the browser's top layer with the
popover
attribute and anchors it to the trigger with CSS anchor positioning, without JavaScript. When it does not fit, it moves to the other side with
position-try-fallbacks. A browser without anchoring opens it unanchored: it still works.
Action menu
With
role="menu", the children with
role="menuitem"
are navigated with the arrow keys,
Home,
End
and the first letter of each action.
Esc
closes the menu and returns focus to the trigger.
<script lang="ts">
const action = signal('')
</script>
<template>
<UIPopover role="menu" placement="bottom-start" bind:offset="6">
<snippet name="trigger">Project actions</snippet>
<UIButton variant="ghost" role="menuitem" on:click="() => (action.value = 'rename')">Rename</UIButton>
<UIButton variant="ghost" role="menuitem" on:click="() => (action.value = 'duplicate')">Duplicate</UIButton>
<UIButton variant="ghost" role="menuitem" on:click="() => (action.value = 'archive')">Archive</UIButton>
</UIPopover>
</template>Lightweight dialog
With
role="dialog"
focus moves into the content when it opens. Use it for brief controls that do not need to block the page; for a task that demands attention, use
Modal. The model controls the opening from code.
Showing published projects.
<script lang="ts">
const opened = signal(false)
</script>
<template>
<UIPopover role="dialog" label="Filter projects" model="opened">
<snippet name="trigger">Filter</snippet>
<UICheckbox label="Published" model="published" />
<UIButton on:click="() => (opened.value = false)">Apply filters</UIButton>
</UIPopover>
</template>Tooltip
With
mode="hint"
the content is a tooltip: it opens when the pointer hovers, after
delay, or when the trigger is focused, and it never receives focus. The trigger references it with
aria-describedby. Write supplementary information in it, never the only explanation of an action.
<UITooltip bind:offset="6">
<snippet name="trigger">Status: draft</snippet>
Only your team can see it until you publish it.
</UITooltip>Your own trigger
The
anchor
snippet replaces the button that UI renders. It receives the attributes of the trigger to apply them with
bind="trigger"; UIPopover finds it by its
id
and adds the keyboard handling and the anchoring.
<UIPopover role="menu">
<snippet name="anchor" params="trigger">
<button type="button" class="avatar" bind="trigger">
<img src="/avatar.png" alt="My account">
</button>
</snippet>
<button role="menuitem">Profile</button>
<button role="menuitem">Sign out</button>
</UIPopover>API
| Name | Type / default | Description |
|---|---|---|
model
|
Signal<boolean>
false
| Two-way opening of the content. |
mode
|
auto | manual | hint
auto
| auto closes on an outside click and with Esc; manual only from the trigger, Esc or the model; hint is a tooltip. |
role
|
menu | dialog | tooltip
| ARIA pattern of the content. With mode=hint, tooltip by default. |
placement
|
top | bottom | left | right, with -start or -end
bottom-start
| Side and alignment relative to the trigger. |
offset
|
number
0
| Distance in pixels from the trigger. |
label
|
string
| Accessible name of the content. Without it, a menu or a dialog is named after the trigger. |
matchWidth
|
boolean
false
| The content is at least as wide as the trigger. |
delay / closeDelay
|
number
300 / 150
| With mode=hint, milliseconds before opening and closing. |
class / triggerClass
|
string
| Classes of the content and of the trigger button. |
triggerId
|
string
| id of the trigger. By default, one that is stable between SSR and hydration. |
It accepts the
default,
trigger
and
anchor
snippets. The
toggle
event receives an object with
open
every time the content opens or closes. With a
ref:, the component exposes
show(),
hide()
and
toggle(). The
UIPopoverProps,
PopoverPlacement
and
PopoverTrigger
types are exported from
@meekojs/ui/runtime.