A compact preview of a linked resource that appears on hover or keyboard focus without hiding essential information.
Pinned project
Hover or focus for a quick preview
import { ArrowUpRight, FolderKanban } from "lucide-react";
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ballmac/hover-card";
export default function HoverCardDemo() {
return (
<div className="w-full max-w-sm rounded-xl border bg-card p-5 shadow-sm">
<p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
Pinned project
</p>
<div className="mt-3 flex items-start gap-3">
<span className="flex size-10 items-center justify-center rounded-lg bg-primary/10 text-primary">
<FolderKanban aria-hidden="true" className="size-5" />
</span>
<div>
<HoverCard defaultOpen openDelay={150}>
<HoverCardTrigger
href="#project"
className="inline-flex items-center gap-1 text-sm font-semibold underline-offset-4 hover:underline"
>
Atlas launch{" "}
<ArrowUpRight aria-hidden="true" className="size-3.5 rtl:-scale-x-100" />
</HoverCardTrigger>
<HoverCardContent>
<p className="text-sm font-semibold">Atlas launch</p>
<p className="mt-1 text-sm leading-relaxed text-muted-foreground">
A shared space for the upcoming product release, from draft
assets to the final checklist.
</p>
<div className="mt-3 flex gap-2 text-xs text-muted-foreground">
<span className="rounded-full bg-muted px-2 py-1">
8 teammates
</span>
<span className="rounded-full bg-muted px-2 py-1">
24 files
</span>
</div>
</HoverCardContent>
</HoverCard>
<p className="mt-1 text-xs text-muted-foreground">
Hover or focus for a quick preview
</p>
</div>
</div>
</div>
);
}Installation
$ pnpm dlx shadcn@latest add @ballmac/hover-cardInstall the dependencies.
$ pnpm add radix-uiCopy the source into your project.
components/ballmac/hover-card.tsx// Ballmac UI: Hover Card. https://ui.ballmac.com/components/hover-card "use client"; import * as React from "react"; import { HoverCard as Primitive } from "radix-ui"; import { cn } from "@/lib/utils"; type HoverCardProps = React.ComponentProps<typeof Primitive.Root>; function HoverCard(props: HoverCardProps) { return <Primitive.Root data-slot="hover-card" {...props} />; } type HoverCardTriggerProps = React.ComponentProps<typeof Primitive.Trigger>; function HoverCardTrigger({ className, ...props }: HoverCardTriggerProps) { return ( <Primitive.Trigger data-slot="hover-card-trigger" className={cn( "rounded-sm outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50", className, )} {...props} /> ); } type HoverCardContentProps = React.ComponentProps<typeof Primitive.Content> & { /** Space in pixels between the trigger and preview. */ sideOffset?: number; }; function HoverCardContent({ className, align = "center", sideOffset = 8, ...props }: HoverCardContentProps) { return ( <Primitive.Portal> <Primitive.Content data-slot="hover-card-content" align={align} sideOffset={sideOffset} collisionPadding={12} className={cn( "z-50 w-[min(20rem,calc(100vw-1.5rem))] rounded-xl border bg-popover p-4 text-popover-foreground shadow-[0_12px_36px_-10px_rgb(0_0_0/0.2)] outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0 data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95 motion-reduce:animate-none", className, )} {...props} /> </Primitive.Portal> ); } export { HoverCard, HoverCardTrigger, HoverCardContent, type HoverCardProps, type HoverCardTriggerProps, type HoverCardContentProps, };Update the import paths to match your project setup.
Usage
import { HoverCard, HoverCardTrigger, HoverCardContent } from "@/components/ballmac/hover-card"The full example is in the Code tab above.
Examples
People preview
Reviewed by Morgan Lee before publication.
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ballmac/hover-card";
export default function HoverCardStates() {
return (
<div className="w-full max-w-xs text-sm">
<p className="text-muted-foreground">
Reviewed by{" "}
<HoverCard openDelay={100}>
<HoverCardTrigger
href="#reviewer"
className="font-medium text-foreground underline decoration-border underline-offset-4"
>
Morgan Lee
</HoverCardTrigger>
<HoverCardContent align="start">
<p className="font-semibold">Morgan Lee</p>
<p className="mt-1 text-muted-foreground">
Product designer · Working on workspace sharing and navigation.
</p>
</HoverCardContent>
</HoverCard>{" "}
before publication.
</p>
</div>
);
}API reference
| Prop | Type | Default |
|---|---|---|
sideOffsetSpace in pixels between the trigger and preview. | number | 8 |
Also accepts the standard attributes of its root element.
Accessibility
| Key | Action |
|---|---|
| Tab | Focuses the linked trigger and opens its preview |
| Escape | Dismisses the preview |
Use with AI
Adds optional context to a link for pointer and keyboard users; the link remains useful without the preview. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Hover Card (@ballmac/hover-card) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Preview a project or profile without navigating
- Show supplementary link metadata
Not for
- Interactive actions inside a panel; use popover
- Information required to complete a task
Registry JSON: https://ui.ballmac.com/r/hover-card.json
Credits
Free to use in personal and commercial projects.
- npm
- radix-ui
- Registry
- shadcn/utils
Pairs well with
Avatar
A Radix avatar with image and initials fallback, three sizes, an optional presence dot, and AvatarGroup for overlapping stacks with a +N overflow counter.
Badge
A small pill label in four variants with an optional status dot and success, warning and error tones. Style links with badgeVariants().
Activity Feed
A compact actor and action feed with timestamps and an empty state.
Audio Player
A podcast-style audio player: play and pause, skip back and forward, a scrubber with chapter ticks, a named current chapter and a speed control. Works with a real file or as a silent demo.