V2 componentsUpdated Oct 14, 2026
Preview Card
A hover card for inline links. The trigger is an anchor inside running text; the card opens after a short delay.
Source: packages/ui/src/components/preview-card
import { PreviewCard } from '@highlight/ui'Change the props; the preview and the snippet update together.
Follow-up posted in #design-crit.
<PreviewCard> <PreviewCard.Trigger href="#" icon={<MessageSquareText />}>#design-crit</PreviewCard.Trigger> <PreviewCard.Content className="w-72"> <p className="text-sm font-medium">Weekly critique thread.</p> </PreviewCard.Content></PreviewCard>In the launch retro, the team agreed to move the release date and split the migration into two steps.
import { PreviewCard } from '@highlight/ui' export default function PreviewCardBasic() { return ( <PreviewCard> <p className="text-foreground-secondary max-w-sm text-sm"> In the <PreviewCard.Trigger href="#">launch retro</PreviewCard.Trigger>, the team agreed to move the release date and split the migration into two steps. </p> <PreviewCard.Content className="w-72"> <p className="text-foreground text-sm font-medium">Launch retro</p> <p className="text-foreground-secondary mt-1 text-xs">Tue, Sep 2 · 45 min · 6 attendees</p> <p className="text-foreground-secondary mt-2 text-sm">Three decisions, two open questions.</p> </PreviewCard.Content> </PreviewCard> )}icon renders inline before the link text.
Follow-up posted in #design-crit.
import { MessageSquareText } from 'lucide-react'import { PreviewCard } from '@highlight/ui' export default function PreviewCardIcon() { return ( <PreviewCard> <p className="text-foreground-secondary max-w-sm text-sm"> Follow-up posted in{' '} <PreviewCard.Trigger href="#" icon={<MessageSquareText />}> #design-crit </PreviewCard.Trigger> . </p> <PreviewCard.Content className="w-72"> <p className="text-foreground text-sm font-medium">#design-crit</p> <p className="text-foreground-secondary mt-1 text-sm">Weekly critique thread. 14 replies.</p> </PreviewCard.Content> </PreviewCard> )}unstyled drops the inline-link look so the trigger can be a pill or a card; render picks its element. The card is Popover’s surface: overlay ground, panel radius, shadow edge, lands at once and fades out.
import { FileText } from 'lucide-react'import { PreviewCard } from '@highlight/ui' export default function PreviewCardAttachment() { return ( <PreviewCard> <PreviewCard.Trigger unstyled delay={300} closeDelay={200} render={<button type="button" />} className="bg-element hover:bg-element-hover text-foreground inline-flex h-7 items-center gap-1.5 rounded-full px-2.5 text-sm" > <FileText className="size-3.5" aria-hidden /> Q3 planning.pdf </PreviewCard.Trigger> <PreviewCard.Content showBackdrop={false} className="w-64" positionerProps={{ side: 'top', align: 'start', sideOffset: 8 }} > <div className="bg-element aspect-[4/3] rounded-[calc(var(--radius-lg)-12px)]" aria-hidden /> <div> <p className="text-foreground text-sm font-medium">Q3 planning.pdf</p> <p className="text-foreground-secondary text-xs">12 pages · 2.4 MB</p> </div> </PreviewCard.Content> </PreviewCard> )}The root, then any compound sub-components. Every part accepts className and forwards the rest of its props to the element or Base UI part beneath it.
PreviewCard- Base UI PreviewCard.Root. Owns open state; opens on hover of its trigger after a delay.
PreviewCard.Trigger- Anchor; href, icon, delay (default 400ms), closeDelay. unstyled drops the link styling for a pill or card trigger.
PreviewCard.Content- Portal + Backdrop + Positioner + Popup. sideOffset, showBackdrop, positionerProps.