Popover

Shows a few lines or a small control anchored to what it is about.

Examples

A short form

Explains a term

Token budget

A preview on hover

Usage

When to use it
Use
A small filter, form or picker anchored to its button.
Use
A hover preview of the thing a link points to.
Use
The explanation of a term beside it (PopoverInfo).
Not for
A list of actions. Use Dropdown menu.
Not for
A one-line label on an icon. Use Tooltip.
Not for
Content that needs the whole page or a decision. Use Dialog.
Not for
Content taller than 320px. Use Sheet.

Rules

10 rules
Must
Use PopoverInfo for an info icon that explains a term, never a Tooltip. Because a tooltip never opens on touch and closes when the pointer leaves, so the definition is unreadable on a phone.
Must
Pass title (it names the button About <title>) or label to PopoverInfo. Because without either every info button is named "More information" and a screen reader cannot tell them apart.
Must
Write plain text only in a PopoverInfo; put a link next to the term or use a Popover. Because focus never enters a PopoverInfo, so a link inside it cannot be reached by keyboard.
Must
Make an openOnHover trigger a link that works on its own, and show nothing the linked page does not. Because touch and keyboard never see a hover preview, so the trigger has to reach the same facts alone.
Never
Put actions in a hover preview. Move them to a DropdownMenu or the linked page. Because a peek closes when the pointer leaves and never opens on touch, so the actions slip away or never appear.
Should
Apply a choice on change and keep the popover open, with a PopoverClose "Done" for touch.
Should
Lead a preview with the name, one line of what it is, then 2 to 4 facts with numbers.
Should
Keep a PopoverInfo to 1 or 2 sentences under 200 characters, with the term as its title.
Never
Add an arrow. Keep the arrowless paper panel 6px off the anchor. Because the 6px offset and overlay shadow already tie the panel to its trigger, and an arrow misses the forced-colors frame.
Should
Let content scroll inside --available-height and move content past 320px tall into a Sheet.

Accessibility

5 checks
Enter or Space on the trigger opens or closes it, and pins a peeking preview; Enter on a hover-preview link follows it.
Tab moves through the content; leaving the panel closes it.
Escape closes this popover only and returns focus to the trigger.
PopoverInfo text is also the button description; the button is named `About <title>`.
Hover previews never peek on touch; PopoverInfo opens by tap.

Edge cases

9 edge cases
Long content and trace ID
The title breaks; the panel scrolls inside the available height.
Hover preview
Waits 600ms, peeks without focus, closes when the pointer leaves; a press inside pins it.
Opened at a screen edge
The panel shifts and flips to stay 16px inside.
Right to left
The panel computes rtl inside an RTL portal scope.
Empty content
Opens a 32px padded box; nothing is announced but "dialog".
Slow save inside
A click outside still dismisses it; the save keeps running.
Ten clicks on the trigger
It toggles ten times and ends where the count says.
Inside a Dialog
Escape closes the popover first; focus returns to its trigger.
Zoom 200%
The panel stays 288px and wraps.

API

8 props
Popover open, defaultOpen, onOpenChange
Base UI default false
PopoverTrigger openOnHover
boolean default false
PopoverTrigger delay, closeDelay
number default 600, 300
PopoverContent side, align, sideOffset
Base UI default 'bottom', 'center', 6
PopoverContent collisionPadding
number default 16
PopoverInfo title
ReactNode
PopoverInfo label
string default 'About ' + title
PopoverInfo side, align
Base UI default 'top', 'center'

Tokens

14 tokens
--surface-page
var(--paper)
--elev-overlay
0 0 0 1px var(--line-frame), 0 12px 24px -12px oklch(var(--text-on-field-c) / var(--shadow-alpha))
--radius-frame
10px
--popover
288px
--space-4
16px
--dur-base
160ms
--dur-fast
120ms
--ease-step
cubic-bezier(0.2, 0, 0, 1)
--type-label
400 14px/16px var(--font-sans)
--type-body-sm
380 13px/18px var(--font-sans)
--text-1
var(--ink)
--text-2
oklch(var(--ink-c) / 0.80)
--text-3
oklch(var(--ink-c) / 0.66)
--popover-sm
256px

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.