Hover card
Eine Vorschaukarte, die sich öffnet, wenn ein Link mit der Maus berührt oder fokussiert wird, für Inhalte, die sehende Nutzer kurz überfliegen können.
pnpm dlx shadcn@latest add https://hextaui.com/r/hover-card.jsonFügt die Komponente, die HextaUI-Theme-Tokens und alle HextaUI-Komponenten hinzu, von denen sie abhängt.
Füge die Theme-Tokens zu deinem globalen CSS hinzu, falls du das noch nicht getan hast.
Installiere die Abhängigkeiten.
pnpm add @base-ui/react cnKopiere den folgenden Code und füge ihn in dein Projekt ein.
components/ui/hover-card.tsx lib/motion.ts Passe die Importpfade an dein Projekt-Setup an.
Eine Hover Card ist eine Vorschau, kein Menü und kein Dialog. Der Trigger bleibt ein normaler Link, daher muss alles in der Karte auch auf der verlinkten Seite stehen.
Seite
Setze side und align auf <HoverCardContent />. Logische Seiten wie inline-end folgen der Leserichtung, und die Karte klappt um oder verschiebt sich, wenn sie den Bildschirm verlassen würde.
Verzögerung
delay und closeDelay am Trigger legen fest, wie lange der Zeiger ruhen muss, bevor sich die Karte öffnet, und wie lange sie nach dem Verlassen verweilt. Der Standard von 600 ms verhindert, dass Karten aufblitzen, wenn der Zeiger über eine Seite fährt.
Inline-Link
Nutze render, um den Trigger zu einem beliebigen Link zu machen, auch einem innerhalb eines Satzes. Bricht ein Link auf zwei Zeilen um, verankert sich die Karte an der Zeile, die du gehovert hast.
Interaktiver Inhalt
Bewege den Zeiger vom Link in die Karte, und sie bleibt geöffnet, sodass Links und Buttons darin angeklickt werden können. Der Weg dazwischen ist großzügig, eine diagonale Bewegung schließt sie also nicht.
Geteilte Karte
Eine Karte bedient viele Links. Erstelle ein Handle mit createHoverCardHandle, gib jedem Trigger ein payload und lies es in der Karte. Beim Wechsel zwischen Namen gleitet die Karte zum neuen Link, statt zu schließen und neu zu öffnen. Der alte Inhalt gleitet in die Richtung hinaus, in die du dich bewegt hast, der neue gleitet herein, und die Höhe passt sich dazwischen sanft an.
Pfeil
arrow fügt einen Zeiger hinzu, der nahtlos an den Rahmen der Karte anschließt. Der Seitenversatz wächst, um Platz dafür zu schaffen, und der Zeiger folgt der Karte, wenn sie umklappt.
Inhalt wird geladen
Starte das Laden in onOpenChange und zeige ein Skeleton, bis die Daten eintreffen. Ändert sich der Inhalt, passt sich die Karte sanft an ihre neue Höhe an, statt zu springen.
Kontrolliert
Übergib open und onOpenChange. Das zweite Argument nennt den Grund der Änderung, etwa trigger-hover, trigger-focus oder escape-key.
Langer Inhalt
Text ohne Umbruchstelle wird in der Karte umgebrochen, und eine Karte, die höher als der Platz neben dem Trigger ist, scrollt, statt den Bildschirm zu verlassen.
Rechts nach links
Die Karte liest die Richtung des Triggers, sodass logische Seiten und Ausrichtung umschlagen und die Skalierungsanimation von der richtigen Ecke aus wächst.
| Taste | Aktion |
|---|---|
| Tab | Das Fokussieren des Triggers öffnet die Karte nach derselben Verzögerung wie beim Hovern. Das Weiterbewegen des Fokus schließt sie. |
| Enter | Folgt dem Link, wie jeder andere Link. |
| Esc | Schließt die Karte. |
- Die Karte ist ein visueller Zusatz für sehende Maus- und Tastaturnutzer. Screenreader hören nur den Link, sodass sie nicht bei jedem Link, an dem sie vorbeikommen, durch eine Vorschau gezwungen werden.
- Auf Touchscreens, wo es kein Hovern gibt, öffnet sich nichts. Ein Tippen folgt dem Link, weshalb das Ziel dieselben Informationen enthalten muss.
- Der Fokus wandert nie in die Karte. Wenn sie Bedienelemente braucht, die per Tastatur erreichbar sein müssen, verwende stattdessen ein Popover.
- Bei reduzierter Bewegung blendet die Karte ohne Skalierung ein und aus, und eine geteilte Karte springt zwischen Links, statt zu gleiten.
Basiert auf der Base UI Preview Card. Jeder Teil akzeptiert die Props der Primitive, die er umschließt.
| Prop | Typ | Standard |
|---|---|---|
open | boolean | – |
defaultOpen | boolean | false |
onOpenChangedetails.reason ist trigger-hover, trigger-focus, trigger-press, outside-press, escape-key, imperative-action oder none. | (open: boolean, details) => void | – |
onOpenChangeCompleteWird aufgerufen, nachdem die Öffnen- oder Schließen-Animation endet. | (open: boolean) => void | – |
handleVerbindet Trigger, die außerhalb der Root gerendert werden. | HoverCardHandle<Payload> | – |
childrenVerwende die Funktionsform, um das Payload des Triggers zu lesen, der die Karte geöffnet hat. | ReactNode | ({ payload }) => ReactNode | – |
actionsRef | RefObject<{ close, unmount }> | – |
| Prop | Typ | Standard |
|---|---|---|
href | string | – |
delayMillisekunden, bevor Hover oder Fokus die Karte öffnet. | number | 600 |
closeDelayMillisekunden, die die Karte nach dem Verlassen geöffnet bleibt. | number | 300 |
handle | HoverCardHandle<Payload> | – |
payloadWird an die Karte übergeben, wenn dieser Trigger sie öffnet. | Payload | – |
renderRendere deinen eigenen Link, etwa <Button variant="link" /> oder einen Router-Link. | ReactElement | (props, state) => ReactElement | <a> |
| Attribut | Beschreibung |
|---|---|
data-slot="hover-card-trigger" | Den Trigger in CSS ansprechen. |
data-popup-open | Vorhanden, solange die Karte dieses Triggers geöffnet ist. |
| Prop | Typ | Standard |
|---|---|---|
side | "top" | "bottom" | "left" | "right" | "inline-start" | "inline-end" | "bottom" |
align | "start" | "center" | "end" | "center" |
arrowZeigt einen Zeiger in Richtung des Triggers. | boolean | false |
sideOffset | number | OffsetFunction | 6, or 10 with arrow |
alignOffset | number | OffsetFunction | 0 |
collisionPaddingAbstand, der zwischen der Karte und dem Viewport-Rand bleibt. | number | Rect | 8 |
collisionAvoidanceOb die Karte bei einer Kollision umklappt, sich verschiebt oder nichts tut. | CollisionAvoidance | – |
sticky | boolean | false |
anchorAn etwas anderem als dem Trigger positionieren. | Element | RefObject | VirtualElement | – |
positionMethod | "absolute" | "fixed" | "absolute" |
disableAnchorTracking | boolean | false |
portalPropsProps für das Portal, etwa container. | HoverCardPortalProps | – |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribut | Beschreibung |
|---|---|
data-slot="hover-card-content" | Die Card in CSS ansprechen. |
data-open | Vorhanden, solange die Karte geöffnet ist. |
data-starting-style | Vorhanden, während die Karte eingeblendet wird. |
data-ending-style | Vorhanden, während die Karte ausgeblendet wird. |
data-instant | focus, wenn der Tastaturfokus die Karte geöffnet hat, dismiss, wenn Escape oder ein Klick nach außen sie geschlossen hat. Die Ausblendanimation wird übersprungen, solange es gesetzt ist. |
data-side | Die Seite, auf der sich die Karte nach Kollisionen eingestellt hat. |
data-align | Die Ausrichtung, auf die es sich eingestellt hat. |
--transform-origin | Der Punkt neben dem Trigger, von dem aus die Skalierungsanimation wächst. |
--available-width | Verbleibender Platz neben dem Trigger. Die Karte wird nie größer als dieser. |
--available-height | Verbleibender Platz oberhalb oder unterhalb. Höherer Inhalt scrollt. |
| Positioner-Attribut | Beschreibung |
|---|---|
data-slot="hover-card-positioner" | Das Element, das sich bewegt. Es gleitet, wenn eine geteilte Karte den Link wechselt. |
data-anchor-hidden | Vorhanden, wenn der Trigger aus dem Sichtbereich scrollt. |
| Innere Teile | Beschreibung |
|---|---|
data-slot="hover-card-viewport" | Umschließt den Inhalt. Trägt data-activation-direction, während eine geteilte Karte den Link wechselt. |
data-slot="hover-card-body" | Dein Inhalt. Seine Höhe passt sich sanft an, wenn er sich ändert. |
data-slot="hover-card-arrow" | Der Zeiger, mit data-side für seinen Rand. |
--popup-height | Wird an der Karte gesetzt, während sie zwischen Links ihre Größe ändert. |
| Prop | Typ | Standard |
|---|---|---|
container | HTMLElement | ShadowRoot | RefObject | null | document.body |
keepMounted | boolean | false |
Gibt ein Handle für losgelöste Trigger zurück. Seine Methoden open(triggerId) und close() steuern die Karte aus Event-Handlern, und isOpen liest ihren Zustand. Übergib ein Typargument, um das Payload zu typisieren.
- MotionDie Easing-Kurven, Dauern und der Reduced-Motion-Check, mit denen jede Komponente animiert, plus Hooks für Größen-Morphs und gleitende Hervorhebungen.
- Alert dialogEin Bestätigungsdialog für destruktive oder wichtige Aktionen, der auf asynchrone Arbeit wartet und auf Smartphones zum Bottom Sheet wird.
- CommandEine durchsuchbare Liste von Aktionen, inline oder als ⌘K-Palette, mit Seiten, Tastenkürzeln und hervorgehobenen Treffern.
- Context menuEin Menü mit Aktionen per Rechtsklick oder langem Drücken, mit Untermenüs, Checkbox- und Radio-Einträgen sowie Halte-Feedback auf Touch-Geräten.
- DialogEin Fenster über der Seite für Formulare und fokussierte Aufgaben, mit fixiertem Header und Footer, Verschachtelung und einem wischbaren Bottom Sheet auf Smartphones.
- DrawerEin Panel, das von jeder Kante hereingleitet und deinem Finger folgt, mit Snap-Punkten, einem funktionierenden Griff und verschachtelten Drawern, die sich stapeln.
In Blocks verwendet
Blocks, die auf Hover card aufbauen.
- Chat SidebarDie Seitenleiste für eine Chat-App. Logo, Suche und Neuer Chat oben, deine eigenen Links darunter, angeheftete Chats, Projekte, die sich aufklappen und ihre Chats zeigen, zuletzt verwendete nach Tag gruppiert und Zeilen mit Hover- und Rechtsklick-Menüs, direktem Umbenennen, Löschen mit Rückgängig und Live-Antwortzuständen.
- ThinkingZeige, was ein Modell tut, während es arbeitet. Eine Shader-Kugel und ein Live-Label, das jedem Schritt folgt, eine einklappbare Spur aus Suchen, Quellen-Chips und Reasoning, dann eine Antwort mit Zitaten direkt neben den Aussagen, die sie stützen.