Preview Card
A non-modal rich preview that opens on hover or focus of an inline link.
Show codeHide code
Installation
import {
PreviewCard,
PreviewCardTrigger,
PreviewCardContent,
PreviewCardBody,
PreviewCardTitle,
PreviewCardDescription,
} from "@makeplane/propel/components/preview-card";Usage
import {
PreviewCard,
PreviewCardBody,
PreviewCardContent,
PreviewCardDescription,
PreviewCardTitle,
PreviewCardTrigger,
} from "@makeplane/propel/components/preview-card";
export default function BasicDemo() {
return (
<PreviewCard>
<PreviewCardTrigger href="https://plane.so">Plane</PreviewCardTrigger>
<PreviewCardContent side="top">
<PreviewCardBody>
<PreviewCardTitle>Plane</PreviewCardTitle>
<PreviewCardDescription>
Open-source project management for issues, sprints, and roadmaps.
</PreviewCardDescription>
</PreviewCardBody>
</PreviewCardContent>
</PreviewCard>
);
}Examples
With image
Show codeHide code
With properties
Show codeHide code
Controlled
A controlled card owns its own visibility through open / onOpenChange, and triggerId selects which trigger it anchors to — letting application code open the card programmatically.
Detached triggers
createPreviewCardHandle() links triggers outside the PreviewCard root to it via the handle prop; each trigger passes a payload the card renders through function-as-children, so one card serves several links.
API Reference
PreviewCard
The preview-card Root — Base UI's context/state provider (renders no element of its own). A behavior-only role, so it lives in `components` (rules 1a, 2); the styled parts live in `elements/preview-card` (and shared `internal/` primitives) and are grafted onto Base UI behavior here.
PreviewCardTrigger
The link that opens the preview card on hover or focus. Renders an `<a>` by default; pass `render` to project the trigger onto your own element (e.g. an `AnchorButton` rendering an `<a>`). Maps 1:1 to `PreviewCard.Trigger`.
PreviewCardContent
Convenience that composes the anchored preview card boilerplate — Base UI portal, positioner, and popup grafted onto Propel's styled surface — so a consumer only writes the trigger and the card body. Pass `side`/`sideOffset`/`align` through to the positioner. No backdrop: unlike Dialog/AlertDialog/Drawer, a preview card is a non-modal, hover-triggered rich tooltip (it opens on hover/focus and closes on pointer-leave/blur, with no focus trap) — dimming the page behind a hover preview reads as a modal takeover it isn't. Base UI ships a `PreviewCard.Backdrop` primitive, but it is purely optional/decorative for apps that want one; it has none of `Dialog.Backdrop`'s click-outside-to-dismiss semantics, so omitting it costs no behavior.
PreviewCardBody
The text content area of the card — typically holds a `PreviewCardTitle` and `PreviewCardDescription` stacked in a column. Owns the padding so a full-bleed `PreviewCardImage` can sit edge-to-edge above it; both the column layout and the padding are "always the same" per the design spec.
PreviewCardTitle
The card's primary heading — `text-body-sm-medium` per the design spec. Truncates to a single line instead of wrapping (Figma titles are nowrap on the 296px card). Diverges from the shared `internal/overlay-title` "md" step (`semibold`) — that step has no current consumer (this family used to adopt it) and is lighter `medium` here per Figma, so it keeps its own recipe (rule 4a: divergent styling stays per-family). When `children` is a string, bakes a native `title` so hover recovers the full text past the truncation; pass `title` explicitly to override (or `title=""` to suppress).
PreviewCardDescription
Supporting description text beneath the title — the shared `internal/overlay-description` recipe at the preview card's `md` size (13px secondary text). The size is fixed here (a `components` default, rule 13).
PreviewCardImage
The thumbnail image shown inside the popup. Bakes in `w-full`, overflow-hidden, and object-cover so the thumbnail always spans the card, clips, and fills its box — these are "always the same" per the design spec. Height is set by the consumer's layout (native `height`, or the image's intrinsic aspect ratio at full width).
PreviewCardEyebrow
Lays an optional leading node beside text on one row. The slot is unstyled flex — pass an icon, a 16×16 filled type indicator (Figma's issue frame), or any other leading glyph. Stories use the shared `internal/Icon` at `tint="secondary" size="md"` as a stand-in. Issue cards put a `PreviewCardEyebrowLabel` identifier (an issue key) here ABOVE a separate `PreviewCardTitle`. Cycle / module / release / intake cards put `PreviewCardTitle` in this row and skip `PreviewCardEyebrowLabel` — those canvases have no muted identifier. Compose it only when the card has a leading glyph; a card without one renders `PreviewCardTitle` directly inside `PreviewCardBody`.
PreviewCardEyebrowLabel
The single-line identifier text inside a `PreviewCardEyebrow` (an issue key, …). Muted one step further than the description and truncates instead of wrapping. Not the card's heading — cycle / module / release / intake names belong in `PreviewCardTitle` (optionally inside the eyebrow, beside the icon). When `children` is a string, bakes a native `title` so hover recovers the full text past the truncation; pass `title` explicitly to override (or `title=""` to suppress).
PreviewCardPropertyGroup
The row of property chips (status, priority, assignee, labels, …) shown beneath the description. A bare flex-wrap row — the chips are the consumer's own components (`PillButton`, `Avatar`, `Badge`, …); this part only supplies the row layout. Never `aria-hidden`: its content is meaningful.
PreviewCardMeta
The muted footer caption closing out the card's text content (a source domain, a relative timestamp, …).