HoverCard(悬停卡片)给在链接上停留的人预览链接的去向,例如个人主页卡片;链接本身仍照常导航。按钮上的简短提示用文字提示,点击打开的交互内容用弹出框。
组合
HoverCard
├── HoverCardTrigger
└── HoverCardContent
└── HoverCardArrow触发器是链接(渲染 <a>,要给 href),按下即跳转;内容是预览,箭头可选。卡片只服务愿意停留的人:触摸与屏幕阅读器用户可能永远不会打开它,卡片里的一切都应在链接目标页面上可达。
用法
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@ui-sdk/react/hover-card";
function Example() {
return (
<HoverCard>
<HoverCardTrigger href="/people/hollis">@hollis</HoverCardTrigger>
<HoverCardContent>
<p>Hollis Grant</p>
<p>支付团队的设计工程师,写表单与动效。</p>
</HoverCardContent>
</HoverCard>
);
}定位与箭头
side 选择打开方向(默认 bottom),align 沿链接对齐 start / center / end;空间不足时翻到另一侧并沿链接平移保持在屏内。sideOffset 是与链接的间距(默认 4px),alignOffset 沿链接偏移,collisionPadding 是与窗口边缘保留的距离,anchor 可改为相对其他元素、选择器或 { x, y } 点定位。
<HoverCardContent side="right" align="start" sideOffset={8} collisionPadding={16}>
<HoverCardArrow />
{/* 预览内容 */}
</HoverCardContent>HoverCardArrow 跟随卡片实际落在的一侧(含翻转后),带相同的 data-side,指向链接中点并与圆角保持 arrowPadding 的距离;够不到中点时带 data-arrow-clamped。
延迟
delay 是指针停留或键盘聚焦多久后打开,默认 600 毫秒;closeDelay 是指针离开链接与卡片后多久关闭,默认 300 毫秒。指针穿过链接与卡片之间的空隙不会关闭。disabled 让卡片不打开,触发器保持普通链接。
<HoverCard delay={300} closeDelay={150} disabled={!previews}>多链接共享一张卡片
信息流或列表不必每个链接渲染一张卡片:把链接和卡片连到同一个 createHoverCardController<Payload>(),每个链接传自己的 payload,内容从 state.payload 读取。只有打开卡片的那个链接带 data-open;controller.open({ payload }) 也能用代码打开。
const profile = createHoverCardController<Person>();
{people.map((person) => (
<HoverCardTrigger
key={person.handle}
controller={profile}
payload={person}
href={person.url}
>
{person.name}
</HoverCardTrigger>
))}
<HoverCard controller={profile}>
<HoverCardContent<Person>>
{(state) => <p>{state.payload?.bio}</p>}
</HoverCardContent>
</HoverCard>卡片里可以有自己的链接:从链接 Tab 会进入打开的卡片,越过卡片末尾则继续到链接之后的页面,焦点离开卡片即关闭。
样式与状态
各部件的 data-slot 为 hover-card-trigger、hover-card-content、hover-card-arrow,开合带 data-open / data-closed;内容与箭头带 data-side 标记实际落点,内容另有 data-starting-style / data-ending-style 供入场退场动画。
内容上的 CSS 变量:--anchor-width / --anchor-height(锚点尺寸)、--available-width / --available-height(到视口边缘的可用空间)、--transform-origin(缩放原点)、--arrow-offset(锚点中心在弹出层边上的位置,可用来定位 ::before 箭头)。
[data-slot="hover-card-content"] {
transform-origin: var(--transform-origin);
transition: opacity 150ms, scale 150ms;
}
[data-slot="hover-card-content"]:is([data-starting-style], [data-ending-style]) {
opacity: 0;
scale: 0.96;
}箭头伸出卡片外,保持外层 overflow 可见;正文要滚动就放在内部元素上。退场播完才卸载,keepMounted 可保留隐藏内容。详见弹出层。
API
| Prop | 类型 | 说明 |
|---|---|---|
open / defaultOpen |
boolean |
受控 / 非受控的开合状态。 |
onOpenChange |
(open, details) => void |
开合变更;details.reason 如 trigger-hover、trigger-focus、escape-key,可 cancel()。 |
onOpenChangeComplete |
(open: boolean) => void |
动画结束后报告开合完成。 |
delay |
number |
悬停或聚焦多久后打开,默认 600ms。 |
closeDelay |
number |
指针离开后多久关闭,默认 300ms。 |
disabled |
boolean |
不打开卡片,链接照常工作。 |
controller |
PopupController |
用 createHoverCardController() 创建,连接外部触发器。 |
HoverCardContent 另有 side、align、sideOffset、alignOffset、collisionPadding、arrowPadding、anchor、container、keepMounted;HoverCardTrigger 有 payload 与 controller。
键盘与无障碍
| 按键 | 行为 |
|---|---|
Tab |
聚焦链接后等待 delay 打开;卡片打开时 Tab 进入卡片 |
Enter |
跟随链接跳转 |
Escape |
关闭卡片,焦点留在链接上 |
键盘聚焦与指针共用同一个 delay,快速 Tab 过一串链接不会每站都闪一张卡片。焦点离开卡片即关闭;Shift+Tab 从卡片回到链接。触摸屏没有悬停,点按直接跳转、不打开卡片。触发器保持普通链接语义,卡片是旁边的预览而非对话框。