MC Hover Card
A card that appears on hover with additional information.
Overview
The mc-hover-card is a lightweight, hover-triggered card component in Micro Club UI. Built on the @base-ui/react preview-card primitive, it delivers accessible hover interactions with a flexible card layout and image/content support.
This component is designed for seamless integration into your codebase, with customizable alignment, offset, and content props.
Preview
import { McHoverCard, McHoverCardContent, McHoverCardTrigger } from '@/components/ui/mc-hover-card';export default function McHoverCardDemo() { return ( <div> <McHoverCard> <McHoverCardTrigger> <button type="button" className="cursor-pointer rounded-full"> hover me </button> </McHoverCardTrigger> <McHoverCardContent textAlign="start" title="John Doe" subtitle="Software Engineer" description="John is a software engineer with 5 years of experience in web development. He loves working with React and TypeScript." /> </McHoverCard> </div> );}Add to Your Project
Deploy the mc-hover-card directly to your components/ui directory:
Manual Setup
If you prefer manual control:
'use client';import { PreviewCard as PreviewCardPrimitive } from '@base-ui/react/preview-card';import { cn } from '@/lib/utils';function McHoverCard({ ...props }: PreviewCardPrimitive.Root.Props) { return <PreviewCardPrimitive.Root data-slot="hover-card" {...props} />;}function McHoverCardTrigger({ ...props }: PreviewCardPrimitive.Trigger.Props) { return <PreviewCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />;}function McHoverCardContent({ className, children, side = 'bottom', sideOffset = 4, align = 'center', alignOffset = 4, textAlign = 'start', imageSrc = null, imageAlt = '', imagePosition, imageposition, title, subtitle, description, ...props}: PreviewCardPrimitive.Popup.Props & Pick<PreviewCardPrimitive.Positioner.Props, 'align' | 'alignOffset' | 'side' | 'sideOffset'> & { textAlign?: 'start' | 'center'; imageSrc?: string | null; imageAlt?: string; imagePosition?: 'top' | 'bottom'; /** @deprecated Use `imagePosition` instead. */ imageposition?: 'top' | 'bottom'; title?: string; subtitle?: string; description?: string; }) { const resolvedImagePosition = imagePosition ?? imageposition ?? 'top'; const hasBuiltInContent = Boolean(imageSrc || title || subtitle || description); return ( <PreviewCardPrimitive.Portal data-slot="hover-card-portal"> <PreviewCardPrimitive.Positioner align={align} alignOffset={alignOffset} side={side} sideOffset={sideOffset} className="isolate z-50" > <PreviewCardPrimitive.Popup data-slot="hover-card-content" className={cn( 'flex w-76 origin-(--transform-origin) flex-col gap-4 rounded-lg bg-card p-4 text-sm text-popover-foreground shadow-md ring-1 ring-border ring-inset outline-hidden duration-100', 'data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2', 'data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', 'data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95', className )} {...props} > {hasBuiltInContent && ( <div className={cn( 'flex gap-4', resolvedImagePosition === 'top' ? 'flex-col' : 'flex-col-reverse' )} > {imageSrc && <img src={imageSrc} alt={imageAlt} className="w-full" />} <div className={cn( 'flex flex-col gap-2', textAlign === 'start' ? 'items-start' : 'items-center' )} > {title && <h4 className="font-semibold text-card-foreground">{title}</h4>} <div className={cn( 'flex flex-col', textAlign === 'start' ? 'items-start' : 'text-center' )} > {subtitle && <p className="font-regular text-card-foreground">{subtitle}</p>} {description && ( <p className="font-regular text-card-foreground">{description}</p> )} </div> </div> </div> )} {children} </PreviewCardPrimitive.Popup> </PreviewCardPrimitive.Positioner> </PreviewCardPrimitive.Portal> );}export { McHoverCard, McHoverCardTrigger, McHoverCardContent };Ensure your @/lib/utils or equivalent classname helper is correctly imported.
Usage
import { McHoverCard, McHoverCardTrigger, McHoverCardContent } from '@/components/ui/mc-hover-card';
export default function Example() {
return (
<McHoverCard>
<McHoverCardTrigger href="#">Hover me</McHoverCardTrigger>
<McHoverCardContent
title="John Doe"
subtitle="Software Engineer"
description="John is a software engineer with 5 years of experience in web development. He loves working with React and TypeScript."
imageSrc="https://github.com/shadcn.png"
/>
</McHoverCard>
);
}McHoverCardTrigger renders an <a>, so give it an href. To use another element, pass it through render instead of nesting it:
<McHoverCardTrigger render={<span tabIndex={0} />}>Hover me</McHoverCardTrigger>Examples
Image Top
<McHoverCard>
<McHoverCardTrigger href="#">Hover me</McHoverCardTrigger>
<McHoverCardContent
textAlign="start"
imageSrc="https://github.com/shadcn.png"
title="John Doe"
subtitle="Software Engineer"
description="John is a software engineer with 5 years of experience in web development. He loves working with React and TypeScript."
/>
</McHoverCard>Bottom Image
<McHoverCard>
<McHoverCardTrigger href="#">Hover me</McHoverCardTrigger>
<McHoverCardContent
imagePosition="bottom"
textAlign="center"
title="Profile"
subtitle="Designer"
description="A bold hover card with the image placed below the content and centered text alignment."
/>
</McHoverCard>Side Placement
<McHoverCard>
<McHoverCardTrigger href="#">Hover me</McHoverCardTrigger>
<McHoverCardContent side="right" sideOffset={8} align="start" alignOffset={8}>
<div className="space-y-2">
<h4 className="font-semibold">Details</h4>
<p className="text-sm">
Use `side`, `sideOffset`, `align`, and `alignOffset` to control the hover card position.
</p>
</div>
</McHoverCardContent>
</McHoverCard>API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | undefined | Main heading displayed inside the hover card |
subtitle | string | undefined | Secondary line of text below the title |
description | string | undefined | Body text shown inside the card |
imageSrc | string | null | null | Optional image URL shown above or below the content |
imageAlt | string | "" | Alternative text for the image |
imagePosition | "top" | "bottom" | "top" | Position of the image relative to the text content |
textAlign | "start" | "center" | "start" | Text alignment for title, subtitle, and description |
side | "top" | "right" | "bottom" | "left" | "inline-start" | "inline-end" | "bottom" | Side where the hover card appears |
sideOffset | number | 4 | Offset distance from the trigger on the specified side |
align | "start" | "center" | "end" | "center" | Alignment of the hover card relative to the trigger |
alignOffset | number | 4 | Offset distance along the alignment axis |
className | string | undefined | Additional CSS classes applied to the visible card |
All other props from @base-ui/react/preview-card are supported.