{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "hover-card",
  "type": "registry:ui",
  "title": "Hover Card",
  "description": "A rich interactive preview surface revealed when the user shows interest in a trigger (hover, keyboard focus, or long-press) — the interactive-content primitive Tooltip explicitly defers to. Built entirely on the native Popover API interest-invoker mechanism: the trigger carries interestfor and the card is popover=\"hint\", so the browser owns hover/focus reveal, ESC dismissal, and implicit-anchor positioning with zero JavaScript. Progressive enhancement: in browsers without interest invokers the trigger stays a working link/button and the card (falling back to popover=\"manual\") simply doesn't appear.",
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/hover-card.tsx",
      "type": "registry:ui",
      "target": "components/ui/hover-card.tsx",
      "content": "/** @jsxImportSource hono/jsx */\nimport type { PropsWithChildren } from \"hono/jsx\"\nimport { cloneElement, isValidElement } from \"hono/jsx\"\nimport { cn, type ClassValue } from \"@/registry/lib/cn\"\n\n// Hover Card — shadcn-htmx, htmx v4 + Tailwind v4.\n//\n// A rich preview surface revealed when the user shows INTEREST in a trigger\n// (hover, keyboard focus, or long-press). Unlike Tooltip — which the APG\n// forbids from holding interactive content and which defers to this primitive\n// — a Hover Card MAY contain links, buttons and other interactive content\n// (e.g. a \"Follow\" button on a user preview).\n//\n// Built entirely on the native Popover API \"interest invoker\" mechanism — zero\n// JS:\n//   - The trigger (an <a> or <button>) carries `interestfor` pointing at the\n//     card's id. The browser reveals the card on hover / focus / long-press\n//     and hides it on lose-interest, with NO state machine of ours.\n//   - The card is `popover=\"hint\"`. Per spec a `hint` popover does NOT light-\n//     dismiss `auto` popovers, can itself be light-dismissed, and responds to\n//     ESC (close request) — exactly the contract we want for a preview card.\n//   - Associating a popover with its interest invoker creates an IMPLICIT\n//     anchor reference, so the card is positioned with CSS `position-area`\n//     relative to the trigger — no JS positioner, unlike registry/ui/popover.tsx\n//     which targets older click-popovers without anchor support.\n//\n// Progressive enhancement: in browsers without interest invokers the trigger\n// is just a normal <a>/<button> (which still works), and `popover=\"hint\"`\n// falls back to `popover=\"manual\"`, so the card simply stays hidden — no error,\n// no broken UI.\n//\n// Refs:\n//   repos/mdn/files/en-us/web/api/popover_api/using_interest_invokers/index.md\n//   repos/mdn/files/en-us/web/api/popover_api/index.md  (popover=\"hint\" state)\n//   repos/mdn/files/en-us/web/html/reference/elements/a/index.md:89  (interestfor)\n//   repos/mdn/files/en-us/web/css/reference/properties/position-area/index.md\n//   repos/shadcn-ui/apps/v4/registry/  (HoverCard anatomy: trigger + content)\n\nexport type HoverCardSide = \"top\" | \"right\" | \"bottom\" | \"left\"\n\n// Layout-only positioning utilities (see app/styles/input.css). They map a\n// side hint onto a CSS `position-area` tile relative to the implicit anchor\n// (the interest invoker), and fall back to a centred placement in browsers\n// without CSS Anchor Positioning. Colour comes from theme tokens below.\nconst sideAnchor: Record<HoverCardSide, string> = {\n  top: \"anchor-hovercard-top\",\n  bottom: \"anchor-hovercard-bottom\",\n  left: \"anchor-hovercard-left\",\n  right: \"anchor-hovercard-right\",\n}\n\nconst contentBase =\n  \"z-50 m-0 w-64 rounded-md border bg-popover p-4 text-sm text-popover-foreground shadow-md outline-none \" +\n  // Native [popover] is display:none until shown; reveal + animate on open.\n  \"[&:not(:popover-open)]:hidden \" +\n  // animate-fade/scale-in keyframed in input.css (shared scn-popover-in).\n  \"[&:popover-open]:animate-[scn-popover-in_120ms_ease-out]\"\n\nexport function hoverCardContentClasses(opts?: {\n  side?: HoverCardSide\n  class?: ClassValue\n}): string {\n  const side = opts?.side ?? \"bottom\"\n  return cn(contentBase, sideAnchor[side], opts?.class)\n}\n\ntype HoverCardTriggerProps = PropsWithChildren<{\n  // Id of the HoverCard this reveals (its `interestfor` target).\n  cardFor: string\n  // Render the wrapped child element (e.g. an <a href>) with the trigger\n  // wiring merged onto it, instead of the default bare <a>. SSR-friendly\n  // equivalent of shadcn's Radix `asChild`.\n  asChild?: boolean\n  // Destination for the default <a>. Interest invokers REVEAL on hover/focus\n  // but the trigger still navigates on click, so a real href keeps it useful\n  // (and functional in non-supporting browsers).\n  href?: string\n  class?: ClassValue\n  id?: string\n}>\n\nexport function HoverCardTrigger(props: HoverCardTriggerProps) {\n  const { cardFor, asChild, href, class: className, id, children, ...rest } = props\n\n  // asChild: clone the single child (an <a>/<button>) and merge the interest-\n  // invoker wiring onto it so the markup the page already has becomes the\n  // trigger — no extra wrapper element in the accessibility tree.\n  if (asChild && isValidElement(children)) {\n    const child = children as any\n    return cloneElement(child, {\n      ...rest,\n      interestfor: cardFor,\n      \"data-slot\": \"hover-card-trigger\",\n      class: cn(child?.props?.class, className),\n    })\n  }\n\n  // Default: a real <a>. interestfor reveals the card on interest; click still\n  // navigates. Anchors are the canonical interest-invoker element (MDN).\n  return (\n    <a\n      id={id}\n      href={href ?? \"#\"}\n      interestfor={cardFor}\n      data-slot=\"hover-card-trigger\"\n      class={cn(className)}\n      {...rest}\n    >\n      {children}\n    </a>\n  )\n}\n\ntype HoverCardProps = PropsWithChildren<{\n  // Required — referenced by the trigger's `interestfor`.\n  id: string\n  // Placement relative to the trigger. Drives `position-area` (anchor) with a\n  // centred fallback. Default \"bottom\".\n  side?: HoverCardSide\n  class?: ClassValue\n  // Forward hx-*, data-*, aria-* (e.g. hx-get to lazily fetch the preview).\n  [key: string]: unknown\n}>\n\nexport function HoverCard(props: HoverCardProps) {\n  const { id, side = \"bottom\", class: className, children, ...rest } = props\n  return (\n    <div\n      id={id}\n      // `hint`: shows on interest, light-dismissable, ESC-closeable, and does\n      // NOT close sibling `auto` popovers. Falls back to `manual` (stays\n      // hidden) in unsupporting browsers — safe progressive enhancement.\n      // Cast: hono/jsx's `popover` type predates the `\"hint\"` state.\n      {...({ popover: \"hint\" } as Record<string, string>)}\n      data-slot=\"hover-card\"\n      data-side={side}\n      class={hoverCardContentClasses({ side, class: className })}\n      {...rest}\n    >\n      {children}\n    </div>\n  )\n}\n"
    },
    {
      "path": "registry/jinja2/hover-card.html",
      "type": "registry:file",
      "target": "templates/components/hover-card.html",
      "content": "{# Hover Card macros — shadcn-htmx, htmx v4 + Tailwind v4.\n\n   A rich preview surface revealed on INTEREST (hover / focus / long-press)\n   of a trigger. Unlike Tooltip it MAY hold interactive content. Built on the\n   native Popover API interest-invoker mechanism — zero JS:\n     - the trigger carries `interestfor` pointing at the card's id;\n     - the card is `popover=\"hint\"` (shows on interest, ESC-closeable, does\n       not light-dismiss `auto` popovers; falls back to `manual` when the\n       feature is unsupported);\n     - the implicit anchor reference lets CSS `position-area` place the card.\n\n   Refs:\n     repos/mdn/files/en-us/web/api/popover_api/using_interest_invokers/index.md\n     repos/mdn/files/en-us/web/api/popover_api/index.md  (popover=\"hint\")\n     repos/mdn/files/en-us/web/css/reference/properties/position-area/index.md\n\n   Usage:\n     {% from \"components/hover-card.html\" import hover_card_trigger, hover_card_open, hover_card_close %}\n\n     {{ hover_card_trigger(\"@productdevbook\", card_for=\"user-card\", href=\"/u/productdevbook\", class_=\"font-medium underline\") }}\n\n     {% call hover_card_open(id=\"user-card\") %}\n       <p>Card body — links and buttons are allowed here.</p>\n     {% endcall %} #}\n\n{% macro hover_card_trigger(label, card_for, href=\"#\", class_=\"\", id=none) %}\n<a {% if id %}id=\"{{ id }}\"{% endif %}\n   href=\"{{ href }}\"\n   interestfor=\"{{ card_for }}\"\n   data-slot=\"hover-card-trigger\"\n   class=\"{{ class_ }}\">{{ label }}</a>\n{% endmacro %}\n\n{% macro hover_card_open(id, side=\"bottom\", extra_class=\"\", attrs={}) %}\n{%- set sides = {\n    \"top\":    \"anchor-hovercard-top\",\n    \"bottom\": \"anchor-hovercard-bottom\",\n    \"left\":   \"anchor-hovercard-left\",\n    \"right\":  \"anchor-hovercard-right\"\n} -%}\n<div id=\"{{ id }}\"\n     popover=\"hint\"\n     data-slot=\"hover-card\" data-side=\"{{ side }}\"\n     class=\"z-50 m-0 w-64 rounded-md border bg-popover p-4 text-sm text-popover-foreground shadow-md outline-none [&:not(:popover-open)]:hidden [&:popover-open]:animate-[scn-popover-in_120ms_ease-out] {{ sides[side] }} {{ extra_class }}\"\n     {% for k, v in attrs.items() %}{{ k|replace('_','-') }}=\"{{ v }}\" {% endfor %}>\n{% endmacro %}\n\n{% macro hover_card_close() %}</div>{% endmacro %}\n"
    },
    {
      "path": "registry/go-templates/hover-card.tmpl",
      "type": "registry:file",
      "target": "components/hover-card.tmpl",
      "content": "{{/*\n  Hover Card templates — shadcn-htmx, htmx v4 + Tailwind v4.\n\n  A rich preview surface revealed on INTEREST (hover / focus / long-press) of a\n  trigger. Unlike Tooltip it MAY hold interactive content. Built on the native\n  Popover API interest-invoker mechanism — zero JS:\n    - the trigger carries `interestfor` pointing at the card's id;\n    - the card is `popover=\"hint\"` (shows on interest, ESC-closeable, does not\n      light-dismiss `auto` popovers; falls back to `manual` when unsupported);\n    - the implicit anchor reference lets CSS `position-area` place the card.\n\n  Refs:\n    repos/mdn/files/en-us/web/api/popover_api/using_interest_invokers/index.md\n    repos/mdn/files/en-us/web/api/popover_api/index.md  (popover=\"hint\")\n    repos/mdn/files/en-us/web/css/reference/properties/position-area/index.md\n\n      type HoverCardArgs struct {\n          ID, Side string\n          Body     template.HTML\n      }\n      type HoverCardTriggerArgs struct {\n          Label, CardFor, Href, Class, ID string\n      }\n*/}}\n\n{{define \"hover_card\"}}\n{{- $side := or .Side \"bottom\" -}}\n{{- $sides := dict \"top\" \"anchor-hovercard-top\" \"bottom\" \"anchor-hovercard-bottom\" \"left\" \"anchor-hovercard-left\" \"right\" \"anchor-hovercard-right\" -}}\n<div id=\"{{.ID}}\" popover=\"hint\" data-slot=\"hover-card\" data-side=\"{{$side}}\"\n     class=\"z-50 m-0 w-64 rounded-md border bg-popover p-4 text-sm text-popover-foreground shadow-md outline-none [&:not(:popover-open)]:hidden [&:popover-open]:animate-[scn-popover-in_120ms_ease-out] {{index $sides $side}}\">\n  {{.Body}}\n</div>\n{{end}}\n\n{{define \"hover_card_trigger\"}}\n<a {{if .ID}}id=\"{{.ID}}\"{{end}}\n   href=\"{{or .Href \"#\"}}\"\n   interestfor=\"{{.CardFor}}\"\n   data-slot=\"hover-card-trigger\"\n   class=\"{{.Class}}\">{{.Label}}</a>\n{{end}}\n"
    },
    {
      "path": "registry/phoenix/hover_card.ex",
      "type": "registry:file",
      "target": "lib/my_app_web/components/hover_card.ex",
      "content": "defmodule ShadcnHtmx.Components.HoverCard do\n  @moduledoc \"\"\"\n  Hover Card — shadcn-htmx, htmx v4 + Tailwind v4 for Phoenix.\n\n  A rich preview surface revealed on INTEREST (hover / focus / long-press) of a\n  trigger. Unlike Tooltip it MAY hold interactive content (links, buttons).\n  Built on the native Popover API interest-invoker mechanism — zero JS:\n\n    * the trigger carries `interestfor` pointing at the card's id;\n    * the card is `popover=\"hint\"` — shows on interest, ESC-closeable, does not\n      light-dismiss `auto` popovers; falls back to `manual` when unsupported;\n    * the implicit anchor reference lets CSS `position-area` place the card.\n\n  Refs:\n    repos/mdn/files/en-us/web/api/popover_api/using_interest_invokers/index.md\n    repos/mdn/files/en-us/web/api/popover_api/index.md  (popover=\"hint\")\n    repos/mdn/files/en-us/web/css/reference/properties/position-area/index.md\n\n  ## Examples\n\n      <.hover_card_trigger card_for=\"user-card\" href=\"/u/productdevbook\" class=\"font-medium underline\">\n        @productdevbook\n      </.hover_card_trigger>\n\n      <.hover_card id=\"user-card\">\n        <p>Card body — links and buttons are allowed here.</p>\n      </.hover_card>\n  \"\"\"\n\n  use Phoenix.Component\n\n  @sides %{\n    \"top\" => \"anchor-hovercard-top\",\n    \"bottom\" => \"anchor-hovercard-bottom\",\n    \"left\" => \"anchor-hovercard-left\",\n    \"right\" => \"anchor-hovercard-right\"\n  }\n\n  attr :id, :string, required: true\n  attr :side, :string, default: \"bottom\", values: ~w(top right bottom left)\n  attr :class, :string, default: nil\n  attr :rest, :global\n  slot :inner_block, required: true\n\n  def hover_card(assigns) do\n    assigns = assign(assigns, :side_class, Map.fetch!(@sides, assigns.side))\n\n    ~H\"\"\"\n    <div\n      id={@id}\n      popover=\"hint\"\n      data-slot=\"hover-card\"\n      data-side={@side}\n      class={[\n        \"z-50 m-0 w-64 rounded-md border bg-popover p-4 text-sm text-popover-foreground shadow-md outline-none\",\n        \"[&:not(:popover-open)]:hidden\",\n        \"[&:popover-open]:animate-[scn-popover-in_120ms_ease-out]\",\n        @side_class,\n        @class\n      ]}\n      {@rest}\n    >\n      {render_slot(@inner_block)}\n    </div>\n    \"\"\"\n  end\n\n  attr :card_for, :string, required: true\n  attr :href, :string, default: \"#\"\n  attr :class, :string, default: nil\n  attr :rest, :global\n  slot :inner_block, required: true\n\n  def hover_card_trigger(assigns) do\n    ~H\"\"\"\n    <a\n      href={@href}\n      interestfor={@card_for}\n      data-slot=\"hover-card-trigger\"\n      class={@class}\n      {@rest}\n    >\n      {render_slot(@inner_block)}\n    </a>\n    \"\"\"\n  end\nend\n"
    },
    {
      "path": "registry/html/hover-card.html",
      "type": "registry:file",
      "target": "snippets/hover-card.html",
      "content": "<!--\n  shadcn-htmx — raw HTML hover-card snippet.\n\n  A rich preview surface revealed on INTEREST (hover / focus / long-press) of a\n  trigger. Unlike a tooltip it MAY hold interactive content (links, buttons).\n  Built on the native Popover API interest-invoker mechanism — NO JS required:\n    - the trigger <a> carries `interestfor` pointing at the card's id;\n    - the card is `popover=\"hint\"` — shows on interest, ESC-closeable, and does\n      not light-dismiss `auto` popovers. It falls back to `popover=\"manual\"`\n      (stays hidden) in browsers without interest-invoker support, so the link\n      still works — pure progressive enhancement.\n    - the implicit anchor reference lets CSS `position-area` (anchor-hovercard-*)\n      place the card relative to the trigger.\n\n  Relies only on theme tokens + the anchor-hovercard-* utilities in styles.css.\n-->\n\n<p>\n  Built by\n  <a href=\"/u/productdevbook\" interestfor=\"user-card\"\n     data-slot=\"hover-card-trigger\"\n     class=\"font-medium text-primary underline-offset-4 hover:underline\">@productdevbook</a>.\n</p>\n\n<div id=\"user-card\" popover=\"hint\" data-slot=\"hover-card\" data-side=\"bottom\"\n  class=\"z-50 m-0 w-64 rounded-md border bg-popover p-4 text-sm text-popover-foreground shadow-md outline-none [&:not(:popover-open)]:hidden [&:popover-open]:animate-[scn-popover-in_120ms_ease-out] anchor-hovercard-bottom\">\n  <div class=\"flex gap-3\">\n    <span data-slot=\"avatar\"\n      class=\"relative inline-flex size-10 shrink-0 overflow-hidden rounded-full bg-muted\">\n      <span class=\"flex size-full items-center justify-center text-sm font-medium text-muted-foreground\">PD</span>\n    </span>\n    <div class=\"space-y-1\">\n      <p class=\"font-semibold\">@productdevbook</p>\n      <p class=\"text-muted-foreground\">Building shadcn-htmx. Web standards first.</p>\n      <button type=\"button\"\n        class=\"mt-1 inline-flex h-7 items-center rounded-md bg-primary px-2.5 text-xs font-medium text-primary-foreground hover:bg-primary/90\">\n        Follow\n      </button>\n    </div>\n  </div>\n</div>\n"
    }
  ]
}
