Hover Card
Podgląd treści, która kryje się za linkiem — osoby, repozytorium, strony — wyświetlany po najechaniu na link lub ustawieniu na nim fokusu.
Zrecenzowane przez @grace 2 godz. temu
import { CalendarDaysIcon, MapPinIcon } from "lucide-react"
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card"
export default function HoverCardDemo() {
return (
<p className="text-sm text-muted-foreground">
Zrecenzowane przez{" "}
<HoverCard>
<HoverCardTrigger
href="#"
className="font-medium text-foreground underline decoration-current/30 underline-offset-4 hover:decoration-current"
>
@grace
</HoverCardTrigger>
<HoverCardContent className="w-80">
<div className="flex gap-4">
<Avatar size="lg">
<AvatarFallback>GH</AvatarFallback>
</Avatar>
<div className="flex min-w-0 flex-1 flex-col gap-2">
<div className="flex items-start justify-between gap-2">
<div className="flex flex-col">
<span className="text-sm font-semibold text-foreground">
Grace Hopper
</span>
<span className="text-xs text-muted-foreground">@grace</span>
</div>
<Button size="xs" variant="outline">
Obserwuj
</Button>
</div>
<p className="text-sm leading-5 text-foreground">
Inżynierka kompilatorów. Design systemy, narzędzia dla programistów i bardzo małe nanosekundy.
</p>
<div className="flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground">
<span className="flex items-center gap-1">
<MapPinIcon className="size-3.5" />
Arlington, Wirginia
</span>
<span className="flex items-center gap-1">
<CalendarDaysIcon className="size-3.5" />
Dołączyła w marcu 2019
</span>
</div>
</div>
</div>
</HoverCardContent>
</HoverCard>{" "}
2 godz. temu
</p>
)
}Instalacja
$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/hover-card.jsonUżycie
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card"<HoverCard>
<HoverCardTrigger href="/people/grace">@grace</HoverCardTrigger>
<HoverCardContent>{/* profile preview */}</HoverCardContent>
</HoverCard>HoverCardTrigger renderuje prawdziwy element <a>: podgląd tylko wzbogaca link, który działa samodzielnie. Komponent jest zbudowany na Preview Card z Base UI.
Przykłady
Podgląd zgłoszenia
Karta podglądu świetnie sprawdza się przy odwołaniach do zgłoszeń, pull requestów i dokumentów: odpowiada na pytanie co to jest i czy jest już zrobione? bez jednego kliknięcia. Ogranicz ją do nazwy, statusu i jednej lub dwóch linijek kontekstu.
Naprawione w DAT-1284, dostępne od wersji 1.4.2.
import { CircleDotIcon, MessageSquareIcon } from "lucide-react"
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import { Badge, BadgeDot } from "@/components/ui/badge"
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card"
export default function HoverCardIssuePreview() {
return (
<p className="max-w-md text-center text-sm leading-6 text-muted-foreground">
Naprawione w{" "}
<HoverCard>
<HoverCardTrigger
href="#"
className="font-mono font-medium text-link underline decoration-current/30 underline-offset-4 hover:decoration-current"
>
DAT-1284
</HoverCardTrigger>
<HoverCardContent className="flex w-80 flex-col gap-3">
<div className="flex items-center justify-between gap-2">
<span className="flex items-center gap-1.5 font-mono text-xs text-muted-foreground">
<CircleDotIcon className="size-3.5 text-success-11" />
DAT-1284
</span>
<Badge variant="success">
<BadgeDot />
Gotowe
</Badge>
</div>
<p className="text-sm leading-5 font-medium text-foreground">
Selektor daty blokuje fokus po otwarciu z popovera
</p>
<div className="flex items-center justify-between text-xs text-muted-foreground">
<span className="flex items-center gap-2">
<Avatar size="xs">
<AvatarFallback>LT</AvatarFallback>
</Avatar>
Linus Torvalds · zamknął 2 dni temu
</span>
<span className="flex items-center gap-1 tabular-nums">
<MessageSquareIcon className="size-3.5" />
14
</span>
</div>
</HoverCardContent>
</HoverCard>
, dostępne od wersji 1.4.2.
</p>
)
}Podgląd linku
Pokaż, dokąd prowadzi link zewnętrzny, zanim ktoś zdecyduje się opuścić stronę. Tutaj karta otwiera się nad linkiem (side="top"), a metadane trafiają do zabarwionej stopki.
prfct jest zbudowany na mui/base-ui, prymitywach bez stylów od twórców Radix, Floating UI i Material UI.
import { GitForkIcon, StarIcon } from "lucide-react"
import { Badge } from "@/components/ui/badge"
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card"
export default function HoverCardLinkPreview() {
return (
<p className="max-w-md text-center text-sm leading-6 text-muted-foreground">
prfct jest zbudowany na{" "}
<HoverCard>
<HoverCardTrigger
href="https://github.com/mui/base-ui"
target="_blank"
rel="noreferrer"
className="font-medium text-link underline decoration-current/30 underline-offset-4 hover:decoration-current"
>
mui/base-ui
</HoverCardTrigger>
<HoverCardContent side="top" className="w-80 p-0">
<div className="flex flex-col gap-2 p-4">
<div className="flex items-center gap-2">
<span className="text-sm font-semibold text-foreground">
mui/base-ui
</span>
<Badge variant="outline" size="sm">
Publiczne
</Badge>
</div>
<p className="text-sm leading-5 text-muted-foreground">
Komponenty UI bez stylów do budowania dostępnych aplikacji webowych i design systemów.
</p>
</div>
<div className="flex items-center gap-4 border-t bg-surface px-4 py-2.5 text-xs text-muted-foreground">
<span className="flex items-center gap-1.5">
<span className="size-2.5 rounded-full bg-blue-9" />
TypeScript
</span>
<span className="flex items-center gap-1 tabular-nums">
<StarIcon className="size-3.5" />
7,9 tys.
</span>
<span className="flex items-center gap-1 tabular-nums">
<GitForkIcon className="size-3.5" />
410
</span>
</div>
</HoverCardContent>
</HoverCard>
, prymitywach bez stylów od twórców Radix, Floating UI i Material UI.
</p>
)
}Opóźnienie
delay (domyślnie 600 ms) i closeDelay (300 ms) na wyzwalaczu decydują, jak celowe musi być najechanie. Długie opóźnienia sprawiają, że karty nie migają, gdy kursor przemierza stronę; krótkie pasują do gęstych list, które ktoś świadomie przegląda.
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card"
const people = [
{
name: "Ada Lovelace",
initials: "AL",
role: "Maszyny analityczne",
delay: 150,
},
{ name: "Alan Turing", initials: "AT", role: "Obliczalność", delay: 600 },
{
name: "Katherine Johnson",
initials: "KJ",
role: "Mechanika orbitalna",
delay: 1000,
},
]
export default function HoverCardDelay() {
return (
<div className="flex flex-wrap items-center justify-center gap-4">
{people.map((person) => (
<HoverCard key={person.name}>
<HoverCardTrigger
href="#"
delay={person.delay}
closeDelay={150}
className="flex flex-col items-center gap-2 rounded-lg p-2 text-sm text-foreground focus-ring hover:bg-accent"
>
<Avatar size="lg">
<AvatarFallback>{person.initials}</AvatarFallback>
</Avatar>
<span className="font-mono text-xs text-muted-foreground">
{person.delay}ms
</span>
</HoverCardTrigger>
<HoverCardContent className="w-60">
<p className="text-sm font-semibold text-foreground">
{person.name}
</p>
<p className="text-sm text-muted-foreground">{person.role}</p>
</HoverCardContent>
</HoverCard>
))}
</div>
)
}Wytyczne
Kiedy używać
- Do podglądu miejsca, do którego prowadzi link: osoby, zgłoszenia, repozytorium, dokumentu.
- Gdy podgląd pomaga zdecydować, czy przejść dalej, ale do tego nie zmusza.
Kiedy nie używać
- Do kluczowych informacji — na urządzeniach dotykowych karty podglądu w ogóle się nie pojawiają, a i bez tego łatwo je przeoczyć. Umieść takie informacje na stronie.
- Do zadań interaktywnych — użyj komponentu Popover, który otwiera się po kliknięciu i może zawierać formularze.
- Do jednolinijkowej etykiety — wystarczy Tooltip.
Podgląd, nie strona
Karta ma potwierdzić: tak, tego właśnie szukasz. Jeśli zaczynasz dodawać do niej zakładki, listy albo kilka akcji, podlinkuj zamiast tego osobną stronę.
Dostępność
Karta podglądu to wizualne udogodnienie dla widzących osób, które korzystają z myszy lub klawiatury. Otwiera się po najechaniu na link i wtedy, gdy link dostaje fokus z klawiatury, ale nie jest ogłaszana, a link się do niej nie odwołuje — osoby korzystające z czytników ekranu poznają więc sam link. Traktuj kartę jako opcjonalny kontekst.
| Klawisz | Działanie |
|---|---|
Tab | Gdy link dostaje fokus, karta otwiera się po upływie opóźnienia. |
Enter | Otwiera link. |
Esc | Zamyka kartę. |
- Link musi się bronić sam. Jego tekst powinien mówić, dokąd prowadzi; karta tylko dodaje kontekst.
- Nie ukrywaj w niej rzeczy kluczowych. Nic, co trzeba wiedzieć lub zrobić, nie może istnieć wyłącznie w karcie podglądu.
- Można na nią najechać. Przesunięcie kursora z linku na kartę utrzymuje ją otwartą, o ile zmieścisz się w
closeDelay.
Dokumentacja API
HoverCard
Komponent główny. Nie renderuje żadnego elementu. Przyjmuje wszystkie propsy PreviewCard.Root z Base UI.
HoverCardTrigger
Link, który otwiera kartę. Renderuje <a> i przyjmuje wszystkie atrybuty odnośnika.
HoverCardContent
Renderuje portal, element pozycjonujący i wyskakujące okienko.