Skip to content
Cynosurev0.5.0
SearchStorybookGitHubnpmColour mode: System. Switch to Light

Components / Overlays

Popover

A small floating panel attached to a button, for options and details that belong to that button: filters, view settings, a quick form. It sits in the browser's top layer, so no parent can clip it.

Opens focuses and returns focus

When to useLink to "When to use"

  • A few related controls that would clutter the page if always visible (filters, display options).
  • Extra detail about one item, with links or buttons in it.

Use something else when:

  • it is a short text hint with no controls: Tooltip
  • it is a list of commands: Menu
  • the person must finish or cancel before continuing: Dialog
  • the content is long or is a whole task: a page or a Sheet

UsageLink to "Usage"

tsx
Copy
import { Button, Checkbox, Popover, Stack } from "@arshad-shah/cynosure";

<Popover label="Filters" trigger={(props) => <Button {...props}>Filters</Button>}>
  <Stack gap="4">
    <Checkbox label="Only errors" />
    <Button variant="solid" tone="accent">Apply</Button>
  </Stack>
</Popover>

trigger is a render prop: spread the props it receives onto your Button or IconButton. They carry the ref, the click handler and the ARIA wiring (aria-expanded, aria-controls, aria-haspopup).

LayersLink to "Layers"

Popovers nest, and Escape closes one layer at a time: an inner popover first, then the outer one. A Tooltip shown inside a popover also closes before the popover does.

Nested close one at atime

Focus and widthLink to "Focus and width"

initialFocus says where focus goes on open: the first control (default), the panel itself, or none. With none, focus stays where it was, so a hint can open beside a field while the person keeps typing; Escape and an outside press still close it, and Tab moves into it when the panel follows the field in the document (render the Popover right after its anchor; with a trigger this is automatic).

Initial focus none keeps the caret while typing

width="trigger" makes the panel at least as wide as its trigger or anchor element, lined up with it; width="fixed" gives it exactly the size preset however short its content is. The default, content, sizes it to its content up to the preset.

Width matches the trigger

PropsLink to "Props"

Every prop of Popover
PropWhat it doesTypeDefault
labelRequired
Accessible name of the panel (a non-modal dialog).string
trigger
Render the trigger: (props) => <Button {...props}>Filters</Button>. Optional with anchor: a popover opened at a point (a canvas click) has no trigger, and is controlled with open.((props: PopoverTriggerProps) => ReactNode)
anchor
Place it at a point ({ x, y } in viewport pixels, such as where a canvas was clicked) or beside any element or virtual anchor, instead of the trigger. Focus goes back to where it was when it closes.VirtualAnchor | { x: number; y: number; }
open
Controlled open state.boolean
defaultOpen
Open at first, when uncontrolled.boolean
onOpenChange
Called with the new state when the trigger, Escape or an outside click opens or closes it.((open: boolean) => void)
placement
Preferred side; flips when it does not fit. start/end follow the reading direction.Placement
size
Panel width preset: the most it grows to (or, with width="fixed", its width).Size
width
How wide the panel is. content (default): as wide as its content, up to the size preset. trigger: at least as wide as the trigger or anchor element, lined up with it (a list under a wide button). fixed: exactly the size preset (--cy-popover-width-{sm,md,lg}), however short the content."content" | "trigger" | "fixed"
initialFocus
Where focus goes on open. first (default): the first control inside, or the panel when it has none. panel: the panel itself, so a screen reader starts at its top. none: focus stays where it is (a field the person is typing in); Escape and outside presses still close it, and Tab from the trigger, or from an anchor the panel follows in the document, moves into it."none" | "first" | "panel"
modal
Keep Tab inside the panel until it closes.boolean
className
Added to the panel, for layout.string
style
Layout only, applied to the panel.CSSProperties
children
The panel's content. Focus moves to its first focusable element on open (see initialFocus).ReactNode

Generated from the type definitions in @arshad-shah/cynosure 0.5.0.

Behaviour and accessibilityLink to "Behaviour and accessibility"

Behaviour and accessibility, table 1
InputResult
Click, tap, Enter, Space on the triggerOpens or closes
EscapeCloses the topmost layer and returns focus to its trigger
Pointer down outsideCloses it
Tab with modalCycles inside the panel
  • Focus: on open, focus moves to the first control inside, or to the panel itself when it has none, so screen readers land in it (initialFocus changes this). On close, focus returns to the trigger unless you already moved it somewhere else.
  • Anchor: an anchor element counts as part of the popover, like the trigger: pressing it is not an outside press.
  • Role: the panel is a dialog named by label. With modal it is aria-modal and keeps Tab inside.
  • Position: the panel prefers placement, flips to the other side when there is no room, and stays inside the viewport. start and end follow the reading direction.
  • Shadow DOM: works inside shadow roots (extension content scripts).
  • Motion: a short fade and scale, removed under reduced motion.

ThemingLink to "Theming"

Tokens: --cy-popover-padding, --cy-popover-radius and --cy-popover-width-{sm,md,lg}.

ts
Copy
createTheme({ components: { popover: { radius: "10px", widthMd: "360px" } } });

Tooltip · Button · Toolbar

These examples run Cynosure 0.5.0 (commit 50ce30b), the version on npm.

Getting started
Theming
Icons
Accessibility
Code editors and highlighters
Using Cynosure without React
Build-tool adapters
Components
Accordion
AppShell
AspectRatio
Breadcrumbs
Calendar
Card
Container
Disclosure
Divider
DocumentViewport
Grid
Inline
PageHeader
PanZoomCanvas
ScrollArea
SideNav
SortableList
SplitPane
Stack
StatusBar
Tabs
TitleBar
Toolbar
Tree
VirtualList
ZoomControls
Announcer
Avatar
Badge
CodeBlock
DescriptionList
EmptyState
ErrorBoundary
Field
Heading
IconTile
Image
Kbd
List
LoadingDots
Notice
ProgressBar
Prose
Skeleton
Stat
Steps
Table
Text
Button
Checkbox
Checklist
ColorField
ColorSwatches
Combobox
CompareSlider
CopyButton
DateField
FileDrop
IconButton
KeyValueEditor
Link
NumberField
Pagination
PathField
RadioGroup
SearchField
SegmentedControl
Select
SelectionFrame
SignaturePad
Slider
Switch
TagInput
TextArea
TextField
ThemeToggle
TimeField
ButtonBase
ChoiceBase
DialogBase
FileInputBase
HiddenValue
Icon
InputBase
LinkBase
LiveRegion
TextAreaBase
ChatMessage
Choice
DataGrid
DeviceFrame
HighlightedText
HotkeyField
ListRow
OptionList
OverlayFrame
QRCode
RowActions
Section
SiteFooter
SiteHeader
SkipLink
SortButton
VisuallyHidden
CommandPalette
ContextMenu
Dialog
FloatingToolbar
HoverCard
Menu
Menubar
Popover
ProgressOverlay
Sheet
Toast
Tooltip
Changelog