Popover
A floating panel anchored to its trigger, with title and description wired for assistive tech.
@emboss/popover
Installation
pnpm dlx shadcn@latest add @emboss/popoverUsage
import { Button } from "@/components/ui/button";
import {
Popover,
PopoverContent,
PopoverDescription,
PopoverTitle,
PopoverTrigger,
} from "@/components/ui/popover";
export function Example() {
return (
<Popover>
<PopoverTrigger render={<Button>Calibrate</Button>} />
<PopoverContent>
<PopoverTitle>Calibration</PopoverTitle>
<PopoverDescription>Adjust the reference level.</PopoverDescription>
</PopoverContent>
</Popover>
);
}API reference
Popover / PopoverTrigger / PopoverContent
Root state, anchored trigger, and the floating panel. Title and description parts label the panel.
| Prop | Type | Default | Description |
|---|---|---|---|
| open / defaultOpen / onOpenChange | boolean / boolean / (open) => void | — | Controlled and uncontrolled open state. |
| sideOffset | number | 8 | Gap between trigger and panel (PopoverContent). |
Keyboard
| Keys | Action |
|---|---|
| Esc | Closes the popover and returns focus. |
Source
View source — popover.tsxHide source — popover.tsx
"use client";
import { Popover as BasePopover } from "@base-ui/react/popover";
import { cn } from "@/lib/utils";
/**
* A floating work surface anchored to its trigger, with title and
* description parts wired to the popup for assistive tech.
*
* @example
* <Popover>
* <PopoverTrigger render={<Button>Calibrate</Button>} />
* <PopoverContent>
* <PopoverTitle>Calibration</PopoverTitle>
* <PopoverDescription>Adjust the reference level.</PopoverDescription>
* </PopoverContent>
* </Popover>
*/
function Popover(props: React.ComponentProps<typeof BasePopover.Root>) {
return <BasePopover.Root {...props} />;
}
function PopoverTrigger(
props: React.ComponentProps<typeof BasePopover.Trigger>,
) {
return <BasePopover.Trigger data-slot="popover-trigger" {...props} />;
}
function PopoverContent({
className,
sideOffset = 8,
children,
...props
}: React.ComponentProps<typeof BasePopover.Popup> & {
sideOffset?: number;
}) {
return (
<BasePopover.Portal>
<BasePopover.Positioner sideOffset={sideOffset} className="z-50">
<BasePopover.Popup
data-slot="popover-content"
className={cn(
"w-72 origin-(--transform-origin) rounded-md bg-surface-2 p-4 text-ink shadow-float-1 transition-[opacity,scale] duration-(--duration-press) ease-(--ease-press) data-ending-style:scale-95 data-ending-style:opacity-0 data-starting-style:scale-95 data-starting-style:opacity-0",
className,
)}
{...props}
>
{children}
</BasePopover.Popup>
</BasePopover.Positioner>
</BasePopover.Portal>
);
}
function PopoverTitle({
className,
...props
}: React.ComponentProps<typeof BasePopover.Title>) {
return (
<BasePopover.Title
data-slot="popover-title"
className={cn("font-medium text-ink", className)}
{...props}
/>
);
}
function PopoverDescription({
className,
...props
}: React.ComponentProps<typeof BasePopover.Description>) {
return (
<BasePopover.Description
data-slot="popover-description"
className={cn("mt-1 text-sm text-ink-muted", className)}
{...props}
/>
);
}
function PopoverClose(props: React.ComponentProps<typeof BasePopover.Close>) {
return <BasePopover.Close data-slot="popover-close" {...props} />;
}
export {
Popover,
PopoverTrigger,
PopoverContent,
PopoverTitle,
PopoverDescription,
PopoverClose,
};