Popover
A sheet that opens beside the control that opened it. Unlike a tooltip it stays up and can be reached, so what is inside can be clicked and typed into.
import { Button, Popover } from 'neba';
<Popover trigger={<Button variant="outline">Share</Button>} title="Share this page">
<TextField size="sm" label="Link" defaultValue="https://…" readOnly />
</Popover>;Props
| Prop | Type | Default | Description |
|---|---|---|---|
| locale | string | — | BCP 47 tag naming the × in that language. Unsupported tags fall back to English |
| trigger | ReactElement | — | The element the popup hangs off and that opens it. One element that accepts a ref and spreads props: every Neba component does |
| sizeshared | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | The type scale, the padding, and how wide the popup may get |
| colorshared | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | Semantic colour role. The sheet is never dyed, so it reaches the edge and the focus ring |
| densityshared | 'default' | 'compact' | 'default' | Padding only |
| title | ReactNode | — | The heading. Rendered as the element that names the popup |
| description | ReactNode | — | A line under the title, and the popup's accessible description |
| children | ReactNode | — | The body |
| side | 'top' | 'right' | 'bottom' | 'left' | 'bottom' | Which edge of the trigger it appears on. Flips to the opposite side when there is no room |
| align | 'start' | 'center' | 'end' | 'center' | Where it sits along that edge |
| sideOffset | number | 6 | Distance from the trigger, in pixels |
| alignOffset | number | 0 | Shift along that edge, in pixels |
| arrow | boolean | false | The little wedge pointing at the trigger. Off by default, unlike Tooltip: this surface is translucent, and a wedge outside the popup cannot carry that backdrop with it |
| open | boolean | — | Whether it is open. With onOpenChange |
| defaultOpen | boolean | false | The initial state of an uncontrolled popover |
| onOpenChange | (open: boolean) => void | — | Called when it opens or closes |
| modal | boolean | 'trap-focus' | false | Whether the page behind is taken away. Off by default, and that is what separates a popover from a Dialog: it sits beside the page, not instead of it |
| dismissible | boolean | true | Whether Escape or a click outside closes it. A PopoverClose still gets through when it is off, so it is never a trap |
| showClose | boolean | false | The × in the corner |
| closeLabel | string | — | Accessible name of the × button |
| width | number | string | — | A hard cap overriding the one size implies. Numbers are pixels |
Native <div> attributes pass through to the popup. Only color, title and children are excluded, since the table above spells them differently.
PopoverClose is Base UI's Popover.Close, re-exported. Give it a render prop and any element dismisses the popup it is inside: <PopoverClose render={<Button>Apply</Button>} />.
The shared axes are described in prop conventions.
Examples
side and align
side is which edge of the trigger the popup sits on; align is where it sits along that edge. The side flips automatically when the window has no room. sideOffset sets the gap and alignOffset shifts it along the edge.
A form in a popup
The popup holds focusable content, so a filter panel, a small form or a colour picker all belong here rather than in a Dialog: the page behind stays readable while the form is filled in. width caps the popup when its content should decide the measure.
Controlled
Pass open with onOpenChange and the caller owns the state, so anything else on the page can open or close it. Without them the popover manages itself and defaultOpen sets the starting state.
arrow
arrow draws a wedge pointing at the trigger. It is off by default: this surface is translucent over a blurred backdrop, and a wedge sticking out past the popup's own box cannot carry that backdrop with it. Turn it on where the trigger is far enough away that the popup has to say what it belongs to.
<Popover arrow trigger={<Button>Details</Button>}>
Anchored to the button it came from.
</Popover>Accessibility
- The popup carries
role="dialog".titlenames it anddescriptiondescribes it, wired witharia-labelledbyandaria-describedby; a popover with neither needs anaria-labelof its own. - Focus moves into the popup when it opens and returns to the trigger when it closes.
- Escape closes it, and so does a click outside.
dismissible={false}cancels both: aPopoverClosestill gets through, so it is never a trap. modalisfalseby default, so the page behind stays scrollable and usable. Use'trap-focus'for a popup that must be answered before anything else is touched.localedecides the ×'s accessible name;closeLabelwrites it out instead.