Components
@alexsmith
Alex Smith@alexsmithProduct designer. Building in public.
Installation
$ pnpm dlx shadcn@latest add @framecn/popover
Usage
import { Popover } from "@/components/ui/popover";<div style={{ position: "relative", display: "inline-block" }}>
<button>@alexsmith</button>
<div
style={{
position: "absolute",
bottom: "calc(100% + 12px)",
left: "50%",
transform: "translateX(-50%)",
}}
>
<Popover state="opened" side="top" width={240}>
Hover-card content
</Popover>
</div>
</div>Popover has no built-in trigger — position it relative to your anchor and supply children for custom content.
Smooth transitions
The docs preview eases a cursor onto an @username chip, opens a hover-card above it via usePopoverTransition, then closes as the cursor leaves:
import { H, W } from "@/lib/customizer-config";
import { Cursor } from "@/components/framecn/cursor";
import { useCursorPath } from "@/components/framecn/use-cursor-path";
import { Popover } from "@/components/framecn/popover";
import { usePopoverTransition } from "@/components/framecn/use-popover-transition";
const CHIP_X = W / 2;
const CHIP_Y = H / 2;
const AWAY_X = W / 2 - 440;
const AWAY_Y = H / 2 - 260;
const cursorStyle = useCursorPath([
{ at: 0, x: 80, y: 60 },
{ at: 28, x: CHIP_X, y: CHIP_Y, duration: 24 },
{ at: 110, x: AWAY_X, y: AWAY_Y, duration: 20 },
]);
const popoverStyle = usePopoverTransition([
{ at: 36, state: "opened", duration: 10 },
{ at: 100, state: "closed", duration: 10 },
]);
<div style={{ position: "relative", display: "inline-block" }}>
@alexsmith
<div style={{ position: "absolute", bottom: "calc(100% + 12px)", left: "50%", transform: "translateX(-50%)" }}>
<Popover style={popoverStyle} side="top" width={240}>
{/* hover-card content */}
</Popover>
</div>
</div>
<Cursor style={cursorStyle} variant="pointer" />;Schedule the popover's opened step shortly after the cursor arrives on the anchor. style takes precedence over state when both are provided.
API Reference
Popover
| Prop | Type | Default |
|---|---|---|
state | "opened" | "closed" | "closed" |
style | PopoverStyle | - (takes precedence over state) |
title | string | - |
description | string | - |
children | React.ReactNode | - |
side | "top" | "bottom" | "left" | "right" | "bottom" |
width | number | 288 |
theme | Partial<FramecnTheme> | - |
className | string | - |