Component
HoverCard
A preview card that opens on hover or keyboard focus of a link (a user, a repository, an item) and shows more about the target. Sighted mouse users get a preview; the link still works alone. Use Tooltip for a short label, Popover for content opened by a click that holds controls.
Xem trước
import { Avatar } from "@/components/ui/avatar"
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card"
import { Link } from "@/components/ui/link"
<HoverCard>
<HoverCardTrigger asChild>
<Link href="#profile">Khanh Nguyen</Link>
</HoverCardTrigger>
<HoverCardContent>
<div className="flex items-start gap-md">
<Avatar alt="" size="lg">KN</Avatar>
<div className="flex flex-col gap-xs">
<p className="text-sm font-medium">Khanh Nguyen</p>
<p className="text-sm text-text-secondary">Product designer building thoughtful tools.</p>
</div>
</div>
</HoverCardContent>
</HoverCard>Cách dùng
Khi nào dùng
- Show read-only details about a linked person or item when a mouse user hovers or a keyboard user focuses it.
- Keep the link useful on touch, where tapping follows the destination without opening the preview.
Khi nào không dùng
- A short label for a control. Use Tooltip.
- A panel with buttons, fields or other controls. Use Popover.
Biến thể
side
On HoverCardContent: preferred side; flips when there is no room.
import { Avatar } from "@/components/ui/avatar"
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card"
import { Link } from "@/components/ui/link"
<HoverCard>
<HoverCardTrigger asChild>
<Link href="#profile">Khanh Nguyen</Link>
</HoverCardTrigger>
<HoverCardContent side="top">
<div className="flex items-start gap-md">
<Avatar alt="" size="lg">KN</Avatar>
<div className="flex flex-col gap-xs">
<p className="text-sm font-medium">Khanh Nguyen</p>
<p className="text-sm text-text-secondary">Product designer building thoughtful tools.</p>
</div>
</div>
</HoverCardContent>
</HoverCard>
<HoverCard>
<HoverCardTrigger asChild>
<Link href="#profile">Khanh Nguyen</Link>
</HoverCardTrigger>
<HoverCardContent side="right">
<div className="flex items-start gap-md">
<Avatar alt="" size="lg">KN</Avatar>
<div className="flex flex-col gap-xs">
<p className="text-sm font-medium">Khanh Nguyen</p>
<p className="text-sm text-text-secondary">Product designer building thoughtful tools.</p>
</div>
</div>
</HoverCardContent>
</HoverCard>
<HoverCard>
<HoverCardTrigger asChild>
<Link href="#profile">Khanh Nguyen</Link>
</HoverCardTrigger>
<HoverCardContent>
<div className="flex items-start gap-md">
<Avatar alt="" size="lg">KN</Avatar>
<div className="flex flex-col gap-xs">
<p className="text-sm font-medium">Khanh Nguyen</p>
<p className="text-sm text-text-secondary">Product designer building thoughtful tools.</p>
</div>
</div>
</HoverCardContent>
</HoverCard>
<HoverCard>
<HoverCardTrigger asChild>
<Link href="#profile">Khanh Nguyen</Link>
</HoverCardTrigger>
<HoverCardContent side="left">
<div className="flex items-start gap-md">
<Avatar alt="" size="lg">KN</Avatar>
<div className="flex flex-col gap-xs">
<p className="text-sm font-medium">Khanh Nguyen</p>
<p className="text-sm text-text-secondary">Product designer building thoughtful tools.</p>
</div>
</div>
</HoverCardContent>
</HoverCard>align
On HoverCardContent: alignment along the trigger.
import { Avatar } from "@/components/ui/avatar"
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card"
import { Link } from "@/components/ui/link"
<HoverCard>
<HoverCardTrigger asChild>
<Link href="#profile">Khanh Nguyen</Link>
</HoverCardTrigger>
<HoverCardContent align="start">
<div className="flex items-start gap-md">
<Avatar alt="" size="lg">KN</Avatar>
<div className="flex flex-col gap-xs">
<p className="text-sm font-medium">Khanh Nguyen</p>
<p className="text-sm text-text-secondary">Product designer building thoughtful tools.</p>
</div>
</div>
</HoverCardContent>
</HoverCard>
<HoverCard>
<HoverCardTrigger asChild>
<Link href="#profile">Khanh Nguyen</Link>
</HoverCardTrigger>
<HoverCardContent>
<div className="flex items-start gap-md">
<Avatar alt="" size="lg">KN</Avatar>
<div className="flex flex-col gap-xs">
<p className="text-sm font-medium">Khanh Nguyen</p>
<p className="text-sm text-text-secondary">Product designer building thoughtful tools.</p>
</div>
</div>
</HoverCardContent>
</HoverCard>
<HoverCard>
<HoverCardTrigger asChild>
<Link href="#profile">Khanh Nguyen</Link>
</HoverCardTrigger>
<HoverCardContent align="end">
<div className="flex items-start gap-md">
<Avatar alt="" size="lg">KN</Avatar>
<div className="flex flex-col gap-xs">
<p className="text-sm font-medium">Khanh Nguyen</p>
<p className="text-sm text-text-secondary">Product designer building thoughtful tools.</p>
</div>
</div>
</HoverCardContent>
</HoverCard>Ví dụ
User preview
import { Avatar } from "@/components/ui/avatar"
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card"
import { Link } from "@/components/ui/link"
<HoverCard>
<HoverCardTrigger asChild>
<Link href="#khanh">Khanh Nguyen</Link>
</HoverCardTrigger>
<HoverCardContent>
<div className="flex items-start gap-md">
<Avatar alt="" size="lg">KN</Avatar>
<div className="flex flex-col gap-xs">
<p className="text-sm font-medium">Khanh Nguyen</p>
<p className="text-sm text-text-secondary">Product designer</p>
</div>
</div>
</HoverCardContent>
</HoverCard>Project preview
import { Badge } from "@/components/ui/badge"
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card"
import { Link } from "@/components/ui/link"
<HoverCard>
<HoverCardTrigger asChild>
<Link href="#workspace">Workspace</Link>
</HoverCardTrigger>
<HoverCardContent side="right" align="start">
<p className="text-sm font-medium">Workspace</p>
<p className="text-sm text-text-secondary">Shared files and project notes for your team.</p>
<Badge>Private</Badge>
</HoverCardContent>
</HoverCard>Short delay
The card opens after 200ms and closes after 100ms. A touch tap still follows the link.
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card"
import { Link } from "@/components/ui/link"
<HoverCard openDelay={200} closeDelay={100}>
<HoverCardTrigger asChild>
<Link href="#report">Quarterly report</Link>
</HoverCardTrigger>
<HoverCardContent side="top" align="end" sideOffset={8}>
<p className="text-sm font-medium">Quarterly report</p>
<p className="text-sm text-text-secondary">Review the latest project updates before opening the document.</p>
</HoverCardContent>
</HoverCard>Nên và không nên
Nên
- Keep the content read-only.
- Grow from the trigger side (origin by side), exit with animate-exit-scale.
Không nên
- Do not put buttons or forms inside; use Popover.
- Do not use on buttons; only links.
Props
| Tên | Kiểu | Giá trị | Mặc định | Mô tả |
|---|---|---|---|---|
| open | boolean | - | - | Open state when controlled. |
| defaultOpen | boolean | - | false | Initial open state. |
| onOpenChange | (open: boolean) => void | - | - | Called when it opens or closes. |
| openDelay | number | - | 600 | Milliseconds before opening. |
| closeDelay | number | - | 300 | Milliseconds before closing. |
| side | enum | top | right | bottom | left | bottom | On HoverCardContent: preferred side; flips when there is no room. |
| align | enum | start | center | end | center | On HoverCardContent: alignment along the trigger. |
| sideOffset | number | - | 4 | On HoverCardContent: distance from the trigger in px. |
| asChild | boolean | - | false | On HoverCardTrigger: apply hover and focus behavior to one real link child. Use true for Link or a. |
| alignOffset | number | - | 0 | On HoverCardContent: offset along the alignment axis in px. |
| avoidCollisions | boolean | - | true | On HoverCardContent: shift or flip the preview to keep it in the viewport. |
| collisionPadding | number | Partial<Record<"top" | "right" | "bottom" | "left", number>> | - | 8 | On HoverCardContent: minimum distance from viewport edges in px. |
Slot
| Tên | Bắt buộc | Mô tả |
|---|---|---|
| trigger | có | The link that opens the card. |
| content | có | Preview content: Avatar, name, short text, Metadata. |
Cấu tạo
| Phần | Mô tả |
|---|---|
| root | HoverCard: holds open state, renders nothing. |
| trigger | HoverCardTrigger: a link, usually Link with asChild. |
| content | HoverCardContent: read-only floating card, 320px wide by default, on the popover stacking layer. |
Khả năng truy cập
Role: none
| Phím | Hành động |
|---|---|
| Tab | Focus on the trigger opens the card after openDelay; leaving focus closes it after closeDelay. |
| Escape | Closes the card; focus stays on the trigger. |
- The content is supplementary: it is not in the tab order and must not hold the only path to an action.
- The trigger is a real link; the card does not change its name. Put the essential info in the link text.
- On touch there is no hover: a tap follows the link and the card does not open.
- The card stays open while the pointer moves from the trigger into it.
Cài đặt
npx werewolf-ui add hover-cardChạy npx werewolf-ui init một lần trong app trước khi thêm component. Init cài token, thêm CSS import và luật cho agent. Add cài component này cùng các dependency và giữ nguyên theme của bạn. Xem Cài đặt để biết yêu cầu và cách cài qua registry.