Popover
An interactive floating panel anchored to its trigger — for content too rich for a tooltip.
"use client";
import { Popover, Button, Text, VStack } from "astralis-ui";
export function PopoverDemo() {
return (
<Popover>
<Popover.Trigger>
<Button variant="outline" colorScheme="gray">Share</Button>
</Popover.Trigger>
<Popover.Content withArrow>
<VStack gap="2" alignItems="stretch" maxW="3xs">
<Popover.Title>Share this page</Popover.Title>
<Popover.Description>
Anyone with the link can view. Popovers hold interactive content —
unlike tooltips.
</Popover.Description>
<Text size="xs" color="subtle">astralis.dev/docs/popover</Text>
<Popover.Close>
<Button size="sm">Copy link</Button>
</Popover.Close>
</VStack>
</Popover.Content>
</Popover>
);
}Import#
import { Popover } from "astralis-ui";
// Popover.Trigger, .Content, .Title, .Description, .CloseUsage#
The trigger toggles on click; the panel takes focus while open and returns it on close. It dismisses on Escape or an outside click (both opt-out). Rule of thumb: hover + read-only → Tooltip; click + interactive → Popover; blocking decision → Modal.
Placement#
side and align compose into twelve anchor positions, with sideOffset /
alignOffset for fine-tuning; collisions flip it automatically.
"use client";
import { Popover, Button, HStack } from "astralis-ui";
export function PopoverPlacement() {
return (
<HStack gap="3" wrap="wrap" justifyContent="center">
<Popover side="top">
<Popover.Trigger>
<Button variant="subtle" size="sm">top</Button>
</Popover.Trigger>
<Popover.Content withArrow>
<Popover.Description>Anchored above.</Popover.Description>
</Popover.Content>
</Popover>
<Popover side="right" align="start">
<Popover.Trigger>
<Button variant="subtle" size="sm">right / start</Button>
</Popover.Trigger>
<Popover.Content withArrow>
<Popover.Description>Side and align compose.</Popover.Description>
</Popover.Content>
</Popover>
<Popover side="bottom" align="end" sideOffset={12}>
<Popover.Trigger>
<Button variant="subtle" size="sm">bottom / end / offset</Button>
</Popover.Trigger>
<Popover.Content withArrow>
<Popover.Description>With an extra 12px gap.</Popover.Description>
</Popover.Content>
</Popover>
</HStack>
);
}Props#
| Prop | Type | Default | Description |
|---|---|---|---|
side | "top" | "right" | "bottom" | "left" | "bottom" | Preferred placement; flips on collision. |
align | "start" | "center" | "end" | "center" | Alignment along the side. |
sideOffset / alignOffset | number | 8 / 0 | Pixel gaps on each axis. |
avoidCollisions | boolean | true | Flip/shift to stay in the viewport. |
closeOnEsc / closeOnOutsidePointer | boolean | true | Dismissal behavior. |
open / defaultOpen / onOpenChange | — | — / false / — | Controlled API. |
Popover.Content takes withArrow; Popover.Trigger and Popover.Close
wrap a single element, like Modal's.
Keyboard#
| Key | Action |
|---|---|
Enter / Space on trigger | Toggle open |
Esc | Close (topmost overlay first) |
Tab | Moves through the panel and out — focus is not trapped |
Accessibility#
- The panel is
role="dialog"; the trigger getsaria-haspopup,aria-expandedandaria-controlsautomatically. - Focus moves into the panel on open and back to the trigger on close.
Title/Descriptionwire the dialog's accessible name and description.