Skip to main content
Graphite UI

Popover

A small panel that opens from a trigger and can hold controls: a filter, a few choices, a short form. Use a Tooltip for a hint, and a Modal when the page should stop until the reader answers.

Contract 2.0.0Kit · 4 sets · 57 variantsOpen in Figma

Live preview

Rendered by the component itself from the same generated tokens as the rest of the site.

Installation

Install it with the shadcn CLI, along with everything it uses. New project? Set it up first, as Quick start shows.

npx shadcn@latest add https://www.graphite-ui.com/r/popover.json
import { Popover } from '@/components/ui/popover'

Anatomy

A trigger you render and a panel the Popover owns. The trigger is a render prop, so it receives aria-expanded, aria-controls and the click handler to spread onto your own Button.

  1. TriggerRequired
  2. ContentRequiredCan include interactive elements.

Variants

Placement chooses the side and align chooses where along the trigger the panel sits; the caret stays on the trigger’s centre either way. The panel does not flip when it runs out of room. Tab tip is the kit’s second set: the open trigger and the panel join into one shape.

Placement: Top
Placement: Bottom
Placement: Left
Placement: Right
Align: Start
Align: End
Tab tip: Start
Tab tip: End

API reference

Generated from the contract, versioned with it, and checked by drift-check. This table cannot describe props the component does not have.

Popover props
PropTypeDefaultNotes
placement'top' | 'bottom' | 'left' | 'right'—The kit's Position. The panel does not flip when it runs out of room.
align'start' | 'center' | 'end'—The kit's Alignment, Center by default as the kit orders it. The caret stays on the trigger's centre; Start and End put it 16 from that edge of the panel.
variant'default' | 'tab-tip'—The kit's Popover and Popover - Tab tip sets. Tab tip opens below only, aligned start or end, and joins the open trigger to the panel with no gap and no caret, the trigger taking the panel's fill.
modalboolean—Whether it traps focus.
defaultOpenboolean—Starts open. For documentation surfaces that need to show the open state; dismissal still comes from the shared Overlay base.
labelstring—The accessible name of a modal Popover's dialog. Ignored without modal, where the panel has no role to name.

Design tokens

Every swatch is live. Change the source color in the header and this table repaints, because it reads the same roles the component does.

  • elevationThe panel and caret fill, elevation-01, the kit's Layer/layer-01; and the open Tab tip trigger, which takes the same fill.
  • shadowThe lift, shadow-overlay, the kit's Shadows/Menu, cast by the panel and caret as one shape (or by trigger and panel together on a Tab tip). No edge.
  • spacingPadding, 16.
  • radiusPanel corner.
  • motionThe entrance fade, shared with the other overlays. No exit: content unmounts on close, which is what keeps the no-nesting throw off the prerender path.

Usage

The contract's prohibitions, written as the choices you will actually face.

Do

  • Use a Popover when the reader needs to act on what is inside it: toggle a filter, pick an option, fill one field.
  • Render the trigger as a Button and spread the props the Popover hands you, so aria-expanded and aria-controls land on the real control.
  • Turn modal on when the content is a small task the reader should finish or dismiss before moving on, and give it a label so the dialog has a name.
  • Choose the placement with the most room around the trigger. The panel will not move itself back on screen.

Don’t

  • Nest a Popover inside another one. The inner one throws as soon as the outer one opens.
  • Give one instance its own way of closing. Dismissal comes from the shared Overlay base, and a Popover that needs another pattern is a different component.
  • Use a Popover for a line of help that appears on hover. That is a Tooltip.
  • Fit a whole workflow into one. Once it needs a title and a footer of actions, it is a Modal.

Accessibility

What the component does for you, and what it leaves to you.

Keyboard
Enter and Space on the trigger open it. The panel follows the trigger in the document, so Tab moves into it. Escape closes it from anywhere.
Roles
The trigger gets aria-expanded and aria-controls. With modal on, the panel is role="dialog" with aria-modal; name it with the label prop. Without modal it has no role, and label is ignored.
Focus
A modal Popover takes focus when it opens and keeps Tab inside it. A non-modal one leaves focus where it was. Either way, focus returns to the trigger when it closes. The content can be controlled from outside: a re-render while it is open leaves focus alone.
Pointer
Pressing the trigger again closes it, and so does a press anywhere outside the panel. There is no close button of its own.
Contrast
The panel is elevation-01 lifted by shadow-overlay, with no edge, as the kit draws every overlay. The shadow is what separates it from the page in Light.
Motion
It fades in on the fast motion step and appears at once under prefers-reduced-motion. It does not animate out.

Figma parity

The kit’s Popover page draws three public sets: the popover itself, a Tab tip, and the Popover item its variants are built from. Placement, align and variant cover the drawn axes; modal and label have no kit axis because behaviour and a name are not drawn.

Kit axes and how they map to code
Kit axisValuesCodeHow it maps
PositionTop · Bottom · Left · RightplacementOne to one.
AlignmentStart · Center · EndalignOne to one, Center by default. Start and End put the caret 16 from that edge, so on the kit’s 32px trigger the panel overhangs by 6.
VisibleTrue · FalsedefaultOpenRuntime state. The kit draws both because Figma has no other way to show it; defaultOpen only chooses where it starts.
Popover item: Caret tipTrue in every variant—Always drawn: 12 by 6 in the panel fill, its base 4 from the trigger.
Popover item: ShadowTrue in every variant—Always drawn: shadow-overlay, with no edge. Shadow=False is drawn by no Popover variant.
Popover item: Zero radiusTrue in every variant—Always square. The 2px corner behind it is drawn by no Popover variant.
SetPopover - Tab tipvariant="tab-tip"Alignment Start · End and Open. The open trigger takes the panel fill and joins it with no gap and no caret, under one shadow. The kit’s trigger is a 48px ghost icon-only button.