Popover
A popover is a dialog that points at its trigger. It holds things the reader can use — a form, a list of actions, a short explanation with a link in it — which is what separates it from a Tooltip, whose contents cannot be reached. For a decision that has to be answered before anything else, use a Modal.
import { Popover, PopoverArrow, PopoverContent, PopoverDialog, PopoverHeading } from "ropav";The first child of Popover is the trigger — any element, no wrapper needed. PopoverContent does the positioning, PopoverDialog is the box, and PopoverArrow is the optional pointer.
popover-basic.vue
<script setup lang="ts">
import {
Button,
Popover,
PopoverArrow,
PopoverContent,
PopoverDialog,
PopoverHeading,
} from "ropav";
</script>
<template>
<Popover>
<Button variant="secondary">Share</Button>
<PopoverContent placement="bottom">
<PopoverArrow />
<PopoverDialog class="dialog">
<PopoverHeading>Share this project</PopoverHeading>
<p class="note">Anyone with the link can read it. Nobody can edit.</p>
</PopoverDialog>
</PopoverContent>
</Popover>
</template>
<style scoped>
.dialog {
max-width: calc(var(--rp-spacing) * 64);
}
.note {
color: var(--rp-muted);
font-size: var(--rp-text-sm);
line-height: var(--rp-text-sm--line-height);
}
</style>default-open starts it open; v-model:is-open, or is-open with an open-change listener, hands the state to the caller.
Where it sits
placement on PopoverContent names the side, and everything else is the fine tuning: offset along the main axis, cross-offset across it, container-padding for the gap kept from the edge of the viewport. should-flip is on by default, so a popover that will not fit below its trigger moves above it rather than off the screen.
The direction is read from the trigger rather than inherited. A popover is rendered at the end of the document, so a logical placement would otherwise resolve against the body's direction and land on the wrong side of a trigger sitting in a right-to-left region.
Modal or not
By default the page behind is inert. is-non-modal leaves it interactive, for a popover that comments on the page rather than interrupting it — a colour picker you keep open while looking at what it changes.
should-close-on-interact-outside filters which outside clicks dismiss it, which is how a popover survives a click on its own trigger or on a toolbar that belongs with it.
Accessibility
- The dialog is labelled by
PopoverHeading. Without one, a screen reader announces a dialog with no name and the reader has to explore it to find out what opened. - Esc closes it and focus returns to the trigger.
is-keyboard-dismiss-disabledturns that off, and should be rare — a popover that cannot be escaped is worse than one that closes too easily. PopoverDialoghandscloseto its slot, for a control inside that has to close the popover after doing something else first.- The arrow points at the trigger for the eye only. What ties the popover to its trigger for everyone else is the focus move, so a popover without one loses nothing.
API
Popover
| Prop | Type | Default | Description |
|---|---|---|---|
defaultOpen | boolean | — | |
isOpen | boolean | — |
Slots: default.
| Event | Payload |
|---|---|
openChange | boolean |
update:isOpen | boolean |
PopoverArrow
Slots: default.
PopoverContent
| Prop | Type | Default | Description |
|---|---|---|---|
arrowBoundaryOffset | number | 0 | Distance kept between the arrow and the corner of the popover. |
containerPadding | number | 12 | Distance kept between the popover and the edge of the viewport. |
crossOffset | number | 0 | Distance along the cross axis. |
isEntering | boolean | — | Forces the entry state, for a caller driving the animation itself. |
isExiting | boolean | — | Forces the exit state. |
isKeyboardDismissDisabled | boolean | false | |
isNonModal | boolean | false | Whether the page behind the popover stays interactive. |
offset | number | 8 | Distance along the main axis, between the popover and its trigger. |
placement | "bottom" | "bottom end" | "bottom left" | "bottom right" | "bottom start" | "end" | "end bottom" | "end top" | "left" | "left bottom" | "left top" | "right" | "right bottom" | "right top" | "start" | "start bottom" | "start top" | "top" | "top end" | "top left" | "top right" | "top start" | "bottom" | Where the popover sits relative to its trigger. |
shouldCloseOnInteractOutside | ((element: Element) => boolean) | — | Filters which outside elements dismiss the popover. |
shouldFlip | boolean | true | Whether the popover may flip to the other side when it does not fit. |
Slots: default.
PopoverDialog
The default slot receives:
| Slot prop | Type |
|---|---|
close | () => void |
PopoverHeading
| Prop | Type | Default | Description |
|---|---|---|---|
level | number | 2 inside a dialog, 3 on its own | Heading level. |
Slots: default.
PopoverTrigger
Slots: default.