Skip to the content
BooleanPress UI

Overlay

Popover

A small panel of extra content or controls next to the element that opened it. The page stays usable.

Import

import { Popover, PopoverTrigger, PopoverContent, PopoverAnchor, PopoverHeader, PopoverTitle, PopoverDescription } from "@booleanpress/ui/popover"

Usage

A popover opens when its trigger is pressed and closes on Escape or a click outside. Use it for a short form, a filter, or an explanation. Use a dialog when the page must wait, and a tooltip for a label that needs no interaction.

import { Button } from "@booleanpress/ui/button"
import { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger } from "@booleanpress/ui/popover"

export function DeliveryStatus() {
  return (
    <Popover>
      <PopoverTrigger asChild>
        <Button variant="outline">Delivery status</Button>
      </PopoverTrigger>
      <PopoverContent>
        <PopoverHeader>
          <PopoverTitle>Delivered</PopoverTitle>
          <PopoverDescription>The mail server accepted this email.</PopoverDescription>
        </PopoverHeader>
      </PopoverContent>
    </Popover>
  )
}

PopoverContent is 288 px wide; set className="w-80" for another width. side and align choose the placement, and it flips to the other side when there is no room. It is uncontrolled, or controlled with open and onOpenChange. PopoverAnchor positions it against an element other than the trigger. There is no per-instance animation switch: motion is set once, in theme.css.

Examples

Basic

A heading and a sentence of explanation.

Placement

side on each of the four sides; it flips when the window has no room.

With a form

A short form inside; focus moves to the first field when it opens.

Controlled

open and onOpenChange let the content close it.

Accessibility

Semantics
The trigger is a button with aria-haspopup="dialog", aria-expanded and aria-controls. The content is a div with role="dialog". It is not modal: the page stays visible to assistive technology, and a click outside closes it.
Labels
The trigger's text names the content. For content that is a form or a group of controls, add aria-label or aria-labelledby to PopoverContent. PopoverTitle and PopoverDescription are plain text, not wired to the dialog.
Focus
Focus moves into the content when it opens, Tab wraps within it while it is open, and focus returns to the trigger when it closes.
Known limits
  • PopoverTitle renders a div, not a heading, and does not name the dialog by itself.

Keyboard

Keyboard
KeyBehaviour
EnterSpaceOn the trigger, opens or closes the popover.
EscapeCloses it and returns focus to the trigger.
TabMoves through the content, wrapping from the last element to the first; Shift+Tab goes the other way. Escape or a click outside closes it.

API

Popover

Renders Radix Popover.Root and passes it every other prop.

Popover props
PropTypeDefaultDescription
defaultOpenbooleanWhether it starts open, when it controls itself.
modalbooleanWhen true, the rest of the page cannot be reached while it is open. false by default.
onOpenChange((open: boolean) => void)Called with true or false when it opens or closes.
openbooleanWhether it is open, when you control it. Pair it with onOpenChange.

PopoverTrigger

Renders Radix Popover.Trigger and passes it every other prop.

PopoverTrigger props
PropTypeDefaultDescription
asChildbooleanRender the child element instead, with this part's behaviour and classes merged onto it.

PopoverContent

Renders Radix Popover.Content and passes it every other prop.

PopoverContent props
PropTypeDefaultDescription
align"center" | "start" | "end"Alignment along that side: start, center or end. center by default.
alignOffsetnumber
arrowPaddingnumber
asChildbooleanRender the child element instead, with this part's behaviour and classes merged onto it.
avoidCollisionsboolean
collisionBoundaryBoundary | Boundary[]
collisionPaddingnumber | Partial<Record<"left" | "right" | "top" | "bottom", number>>
deferPointerDownOutsidebooleanWhen true, a 'pointerdown' event outside of the layered element will wait for the interaction's click event before dispatching, allowing third-party code to stop propagation of later events and cancel dismissal.
forceMounttrueUsed to force mounting when more control is needed. Useful when controlling animation with React animation libraries.
hideWhenDetachedboolean
onCloseAutoFocus((event: Event) => void)Event handler called when auto-focusing on close. Can be prevented.
onEscapeKeyDown((event: KeyboardEvent) => void)Event handler called when the escape key is down. Can be prevented.
onFocusOutside((event: FocusOutsideEvent) => void)Event handler called when the focus moves outside of the DismissableLayer. Can be prevented.
onInteractOutside((event: FocusOutsideEvent | PointerDownOutsideEvent) => void)Event handler called when an interaction happens outside the DismissableLayer. Specifically, when a pointerdown event happens outside or focus moves outside of it. Can be prevented.
onOpenAutoFocus((event: Event) => void)Event handler called when auto-focusing on open. Can be prevented.
onPointerDownOutside((event: PointerDownOutsideEvent) => void)Event handler called when the a pointerdown event happens outside of the DismissableLayer. Can be prevented.
side"left" | "right" | "top" | "bottom"The preferred side: top, right, bottom or left. bottom by default; it flips when there is no room.
sideOffsetnumber4Distance in pixels from the trigger. 4 by default.
sticky"partial" | "always"
updatePositionStrategy"always" | "optimized"

PopoverAnchor

Renders Radix Popover.Anchor and passes it every other prop.

PopoverAnchor props
PropTypeDefaultDescription
asChildbooleanRender the child element instead, with this part's behaviour and classes merged onto it.
virtualRefRefObject<Measurable | null>

PopoverHeader

Renders a div and passes it every other prop.

PopoverTitle

Renders a h2 and passes it every other prop.

PopoverDescription

Renders a p and passes it every other prop.

Every part takes className, merged with its defaults by cn(), and ref, which reaches the element it renders.

Data attributes: data-slot="popover" (Popover), data-slot="popover-trigger" (PopoverTrigger), data-slot="popover-content" (PopoverContent), data-slot="popover-anchor" (PopoverAnchor), data-slot="popover-header" (PopoverHeader), data-slot="popover-title" (PopoverTitle), data-slot="popover-description" (PopoverDescription).

Theming

The panel is a floating surface: --popover and --popover-foreground, with the border token and a medium shadow.

The tokens its classes read. Change their values in your theme, and it follows.

Theme tokens
TokenUsed for
--muted-foregroundtext
--popoverbackground
--popover-foregroundtext