Skip to content

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.

ts
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
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.

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-disabled turns that off, and should be rare — a popover that cannot be escaped is worse than one that closes too easily.
  • PopoverDialog hands close to 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

PropTypeDefaultDescription
defaultOpenboolean—
isOpenboolean—

Slots: default.

EventPayload
openChangeboolean
update:isOpenboolean

PopoverArrow

Slots: default.

PopoverContent

PropTypeDefaultDescription
arrowBoundaryOffsetnumber0Distance kept between the arrow and the corner of the popover.
containerPaddingnumber12Distance kept between the popover and the edge of the viewport.
crossOffsetnumber0Distance along the cross axis.
isEnteringboolean—Forces the entry state, for a caller driving the animation itself.
isExitingboolean—Forces the exit state.
isKeyboardDismissDisabledbooleanfalse
isNonModalbooleanfalseWhether the page behind the popover stays interactive.
offsetnumber8Distance 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.
shouldFlipbooleantrueWhether the popover may flip to the other side when it does not fit.

Slots: default.

PopoverDialog

The default slot receives:

Slot propType
close() => void

PopoverHeading

PropTypeDefaultDescription
levelnumber2 inside a dialog, 3 on its ownHeading level.

Slots: default.

PopoverTrigger

Slots: default.