Installation
npm install @kuzenbo/core
Usage
import { PreviewCard } from "@kuzenbo/core";
function Example() {
return (
<PreviewCard>
<PreviewCard.Trigger asChild>
<a href="#">Hover me</a>
</PreviewCard.Trigger>
<PreviewCard.Portal>
<PreviewCard.Positioner>
<PreviewCard.Popup>
<PreviewCard.Content>
<PreviewCard.Viewport>
<div className="p-4">
<h3 className="font-semibold">Preview Title</h3>
<p className="text-sm text-muted-foreground">
Preview description and additional content.
</p>
</div>
</PreviewCard.Viewport>
<PreviewCard.Arrow />
</PreviewCard.Content>
</PreviewCard.Popup>
</PreviewCard.Positioner>
</PreviewCard.Portal>
</PreviewCard>
);
}
Props
PreviewCard
Default open state (uncontrolled mode).
Callback when open state changes.
Delay in ms before opening on hover.
Delay in ms before closing.
PreviewCard.Trigger
Additional CSS classes to apply.
PreviewCard.Content
side
'top' | 'right' | 'bottom' | 'left'
default:"'bottom'"
Preferred side of the trigger to render.
align
'start' | 'center' | 'end'
default:"'center'"
Alignment relative to the trigger.
Distance from the trigger.
Additional CSS classes to apply.
PreviewCard.Arrow
Additional CSS classes to apply.
Advanced Patterns
Link Preview
function LinkPreview({ href, children }: { href: string; children: ReactNode }) {
const [preview, setPreview] = useState<{ title: string; description: string } | null>(null);
return (
<PreviewCard>
<PreviewCard.Trigger asChild>
<a href={href} className="underline">
{children}
</a>
</PreviewCard.Trigger>
<PreviewCard.Portal>
<PreviewCard.Positioner>
<PreviewCard.Popup>
<PreviewCard.Content className="w-80">
<PreviewCard.Viewport>
{preview ? (
<div className="p-4 space-y-2">
<h4 className="font-semibold">{preview.title}</h4>
<p className="text-sm text-muted-foreground">
{preview.description}
</p>
</div>
) : (
<div className="p-4">Loading...</div>
)}
</PreviewCard.Viewport>
<PreviewCard.Arrow />
</PreviewCard.Content>
</PreviewCard.Popup>
</PreviewCard.Positioner>
</PreviewCard.Portal>
</PreviewCard>
);
}
User Profile Preview
<PreviewCard>
<PreviewCard.Trigger asChild>
<button className="text-primary hover:underline">
@username
</button>
</PreviewCard.Trigger>
<PreviewCard.Portal>
<PreviewCard.Positioner>
<PreviewCard.Popup>
<PreviewCard.Content>
<PreviewCard.Viewport>
<div className="p-4 space-y-3">
<div className="flex items-center gap-3">
<Avatar>
<Avatar.Image src="/avatar.jpg" />
<Avatar.Fallback>UN</Avatar.Fallback>
</Avatar>
<div>
<p className="font-semibold">User Name</p>
<p className="text-sm text-muted-foreground">@username</p>
</div>
</div>
<p className="text-sm">Bio and description</p>
<div className="flex gap-4 text-sm">
<span><strong>123</strong> followers</span>
<span><strong>456</strong> following</span>
</div>
</div>
</PreviewCard.Viewport>
</PreviewCard.Content>
</PreviewCard.Popup>
</PreviewCard.Positioner>
</PreviewCard.Portal>
</PreviewCard>
Controlled Preview
const [open, setOpen] = useState(false);
<PreviewCard open={open} onOpenChange={setOpen}>
{/* ... */}
</PreviewCard>