{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "hover-card",
  "type": "registry:ui",
  "title": "Hover Card",
  "description": "@interlace/ui — hover-card primitive (shadcn-compatible).",
  "dependencies": [
    "@base-ui/react"
  ],
  "registryDependencies": [
    "theme"
  ],
  "files": [
    {
      "path": "registry/interlace-ui/hover-card.tsx",
      "target": "components/ui/hover-card.tsx",
      "type": "registry:ui",
      "content": "'use client';\n\n/**\n * @interlace/ui — HoverCard\n *\n * A rich, non-modal overlay that opens on pointer-hover (and keyboard focus)\n * of its trigger, used to preview the *contents* of a target — author cards,\n * repo cards, link previews. Distinct from `Tooltip` (short, one-line, no\n * interactive content) and `Popover` (click-driven, can hold a form).\n *\n * Under the hood this wraps Base UI's `PreviewCard` family (Base UI's\n * canonical name for the hover-card pattern). We re-export it under the\n * Radix / shadcn name `HoverCard` so consumers swap drop-in.\n *\n * ## Anatomy\n *\n *   HoverCard         (PreviewCard.Root, data-slot=\"hover-card\",\n *                       data-min-viewport=768 on the trigger surface\n *                       via the Popup — see below)\n *     ├─ HoverCardTrigger (anchor on the page that opens the card)\n *     └─ HoverCardPortal\n *         └─ HoverCardPositioner\n *             └─ HoverCardPopup (the surface — rounded card + shadow)\n *\n * Base UI's `Root` is a logical container (no DOM), so we hang\n * `data-min-viewport` on the `Popup` (the actual rendered surface) and also\n * forward it on `Root` for inspection-time tooling that walks the parts tree.\n *\n * ## MIN_VIEWPORT — 768 (the headline trade-off)\n *\n * **Hover is a desktop input.** Touch devices have no `:hover` state — a tap\n * on a hover-trigger either does nothing (until a second tap) or fires the\n * link the trigger wraps, surprising the user. iOS/Android emulate hover\n * with a long-press that conflicts with text-selection and context menus.\n * For that reason this primitive is gated to viewports ≥ 768 CSS px (the\n * common tablet/desktop floor — DESIGN_PRINCIPLES #14).\n *\n * Below 768, **do not use HoverCard.** Reach for one of:\n *\n *   - `Tooltip`  — if the disclosure is a short, non-interactive label;\n *                  Base UI's Tooltip already handles keyboard + touch\n *                  fall-through correctly.\n *   - `Popover`  — if the disclosure is rich (links, buttons, longer copy)\n *                  and needs a deliberate, tap-driven open.\n *\n * The preflight contract draws a dev-mode outline on any primitive whose\n * `data-min-viewport` exceeds the current viewport, so a HoverCard left in\n * the tree at phone widths will visibly warn during local dev and fail the\n * Playwright `min-viewport-respected` lock.\n *\n * | Rule | Concept                          | Where in this file                                          |\n * | ---- | -------------------------------- | ----------------------------------------------------------- |\n * | R4   | Extends Base UI part props       | Each wrapper extends `React.ComponentProps<typeof BasePreviewCard.X>` |\n * | R6   | data-slot on every part          | hover-card / -trigger / -portal / -positioner / -popup       |\n * | R7   | className merged + ...rest       | `cn(BASE, className)` + `{...props}` on the Popup            |\n * | R12  | Reuse over wrap                  | Wraps Base UI; no bespoke open-state machine                 |\n * | R13  | Ecosystem first                  | Base UI `PreviewCard` owns positioning, focus, dismissal     |\n * | R14  | Declares min viewport            | `data-min-viewport={String(MIN_VIEWPORT)}` (Root + Popup)    |\n * | R17  | API parity                       | Surface name `HoverCard` mirrors Radix / shadcn              |\n * | R18  | Tailwind only                    | Zero inline `style`; Tailwind classes only                   |\n * | R19  | Tokens only                      | `rounded-md`, `bg-card`, `text-card-foreground`, `p-(--spacing-md)`, `max-w-80`, `shadow-lg` |\n * | R20  | AA contrast                      | `bg-card` / `text-card-foreground` is an AA-cleared token pair |\n * | R25  | Client component                 | Required — Base UI's PreviewCard ships client hooks          |\n * | R26  | A11y from upstream               | Base UI handles aria-describedby + focus + dismissal         |\n */\n\nimport * as React from 'react';\nimport { PreviewCard as BasePreviewCard } from '@base-ui/react/preview-card';\n\nimport { cn } from '@/lib/utils';\n\n/**\n * Minimum viable viewport (CSS px) for this primitive.\n *\n * 768 because hover is touch-unfriendly; on phones consumers should reach\n * for Popover or Tooltip instead. See the JSDoc header for the full\n * trade-off and the recommended fall-back primitives.\n */\nexport const MIN_VIEWPORT = 768 as const;\n\n// ─────────────────────────────────────────────────────────────────\n// HoverCard (root — logical, no DOM)\n//\n// Base UI's `PreviewCard.Root` is a logical container with no DOM output,\n// so it accepts neither a `ref` nor `data-*` attributes. We re-export it\n// directly (no wrapper function) under the Radix / shadcn name `HoverCard`\n// for drop-in parity. `data-min-viewport` cannot live here — it hangs on\n// the Popup (the actual rendered surface) and inspection-time tooling\n// walks the parts tree from there. R12: slot the primitive directly.\n// ─────────────────────────────────────────────────────────────────\nconst HoverCard = BasePreviewCard.Root;\n\n// ─────────────────────────────────────────────────────────────────\n// HoverCardTrigger (the anchor on the page)\n// ─────────────────────────────────────────────────────────────────\nconst HoverCardTrigger = React.forwardRef<\n  HTMLAnchorElement,\n  React.ComponentProps<typeof BasePreviewCard.Trigger>\n>((props, ref) => {\n  return (\n    <BasePreviewCard.Trigger\n      ref={ref}\n      data-slot=\"hover-card-trigger\"\n      {...props}\n    />\n  );\n});\nHoverCardTrigger.displayName = 'HoverCardTrigger';\n\n// ─────────────────────────────────────────────────────────────────\n// HoverCardPortal (escapes overflow / stacking contexts)\n// ─────────────────────────────────────────────────────────────────\nconst HoverCardPortal = React.forwardRef<\n  React.ElementRef<typeof BasePreviewCard.Portal>,\n  React.ComponentProps<typeof BasePreviewCard.Portal>\n>((props, ref) => {\n  return (\n    <BasePreviewCard.Portal\n      ref={ref}\n      data-slot=\"hover-card-portal\"\n      {...props}\n    />\n  );\n});\nHoverCardPortal.displayName = 'HoverCardPortal';\n\n// ─────────────────────────────────────────────────────────────────\n// HoverCardPositioner (floating-ui placement)\n// ─────────────────────────────────────────────────────────────────\nconst HoverCardPositioner = React.forwardRef<\n  React.ElementRef<typeof BasePreviewCard.Positioner>,\n  React.ComponentProps<typeof BasePreviewCard.Positioner>\n>((props, ref) => {\n  return (\n    <BasePreviewCard.Positioner\n      ref={ref}\n      data-slot=\"hover-card-positioner\"\n      {...props}\n    />\n  );\n});\nHoverCardPositioner.displayName = 'HoverCardPositioner';\n\n// ─────────────────────────────────────────────────────────────────\n// HoverCardPopup (the rendered surface)\n// ─────────────────────────────────────────────────────────────────\nconst HoverCardPopup = React.forwardRef<\n  React.ElementRef<typeof BasePreviewCard.Popup>,\n  React.ComponentProps<typeof BasePreviewCard.Popup>\n>(({ className, ...props }, ref) => {\n  return (\n    <BasePreviewCard.Popup\n      ref={ref}\n      data-slot=\"hover-card-popup\"\n      data-min-viewport={String(MIN_VIEWPORT)}\n      className={cn(\n        'rounded-md border bg-card text-card-foreground p-(--spacing-md) max-w-80 shadow-lg',\n        className,\n      )}\n      {...props}\n    />\n  );\n});\nHoverCardPopup.displayName = 'HoverCardPopup';\n\nexport {\n  HoverCard,\n  HoverCardTrigger,\n  HoverCardPortal,\n  HoverCardPositioner,\n  HoverCardPopup,\n};\n"
    }
  ]
}
