{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "hover-card",
  "type": "registry:component",
  "title": "Hover card",
  "description": "Previews linked context on hover or keyboard focus.",
  "registryDependencies": [
    "https://vlak.dev/r/vlak-base.json",
    "https://vlak.dev/r/inter.json",
    "https://vlak.dev/r/vlak-lib.json",
    "https://vlak.dev/r/tooltip.json"
  ],
  "dependencies": [
    "@stylexjs/stylex"
  ],
  "devDependencies": [
    "@stylexjs/babel-plugin"
  ],
  "docs": "Vlak leaves are StyleX. Compile them with @stylexjs/babel-plugin (Vite: @stylexjs/unplugin, Next: @stylexjs/nextjs-plugin). If you would rather not run a compiler, import @noorddev/vlak-react instead: it ships precompiled with one stylesheet.",
  "files": [
    {
      "path": "vlak/hover-card.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as stylex from \"@stylexjs/stylex\";\nimport { vlak, mq } from \"./tokens.stylex\";\nimport { rs } from \"./rs\";\nimport { describeTrigger } from \"./tooltip\";\n\nexport interface HoverCardProps extends React.HTMLAttributes<HTMLSpanElement> {\n  /** A focusable element is used as is; anything else gets one tab stop. */\n  trigger: React.ReactNode;\n  /** Keep the panel shown regardless of hover or focus: for docs, tests, and static previews. */\n  open?: boolean;\n}\n\nconst styles = stylex.create({\n  root: {\n    position: \"relative\",\n    display: \"inline-block\",\n  },\n  panel: {\n    position: \"absolute\",\n    top: \"calc(100% + 8px)\",\n    insetInlineStart: 0,\n    zIndex: vlak.zFloat,\n    width: {\n      default: \"16.25rem\",\n      [mq.phone]: \"min(280px, calc(100vw - 32px))\",\n    },\n    backgroundColor: vlak.paper,\n    borderWidth: vlak.hairline,\n    borderStyle: \"solid\",\n    borderColor: vlak.divider,\n    borderRadius: {\n      default: vlak.radius,\n      [mq.phone]: vlak.radius,\n    },\n    boxShadow: {\n      default: \"0 8px 24px rgba(0,0,0,0.06)\",\n      [mq.phone]: \"none\",\n    },\n    padding: \"0.875rem\",\n    fontSize: {\n      default: \"0.8125rem\",\n      [mq.phone]: \"0.9375rem\",\n    },\n    lineHeight: 1.45,\n    letterSpacing: \"-0.01em\",\n    color: vlak.gray,\n    opacity: {\n      default: 0,\n      [stylex.when.ancestor(\":hover\")]: {\n        default: null,\n        [mq.hover]: 1,\n      },\n      [stylex.when.ancestor(\":focus-within\")]: 1,\n      [stylex.when.ancestor(\":active\")]: {\n        default: null,\n        [mq.touch]: 1,\n      },\n    },\n    visibility: {\n      default: \"hidden\",\n      [stylex.when.ancestor(\":hover\")]: {\n        default: null,\n        [mq.hover]: \"visible\",\n      },\n      [stylex.when.ancestor(\":focus-within\")]: \"visible\",\n      [stylex.when.ancestor(\":active\")]: {\n        default: null,\n        [mq.touch]: \"visible\",\n      },\n    },\n    transition: {\n      default: vlak.transition,\n      [mq.reduce]: \"none\",\n    },\n    // Bridges the 8px gap so the pointer can move onto the panel.\n    \"::before\": {\n      content: '\"\"',\n      position: \"absolute\",\n      bottom: \"100%\",\n      insetInlineStart: 0,\n      insetInlineEnd: 0,\n      height: \"0.5rem\",\n    },\n  },\n  open: {\n    opacity: 1,\n    visibility: \"visible\",\n  },\n});\n\n/**\n * Preview panel on hover or keyboard focus. CSS shows it; the panel\n * describes the trigger, and Escape hides it until the pointer leaves.\n */\nexport const HoverCard = React.forwardRef<HTMLSpanElement, HoverCardProps>(function HoverCard(\n  { trigger, open, className, style, children, onKeyDown, onPointerLeave, onBlur, ...props },\n  ref,\n) {\n  const id = React.useId();\n  const [dismissed, setDismissed] = React.useState(false);\n  const sx = rs([\"rs-hover-card\", className], styles.root, stylex.defaultMarker());\n  const panel = rs([\"rs-hover-card-panel\", open && \"rs-hover-card-open\"], styles.panel, open && styles.open);\n  const anchor = React.isValidElement(trigger) ? (\n    describeTrigger(trigger, id)\n  ) : (\n    <span tabIndex={0} aria-describedby={id}>\n      {trigger}\n    </span>\n  );\n  return (\n    <span\n      ref={ref}\n      {...props}\n      className={sx.className}\n      style={{ ...sx.style, ...style }}\n      onKeyDown={(e) => {\n        onKeyDown?.(e);\n        if (e.key === \"Escape\") setDismissed(true);\n      }}\n      onPointerLeave={(e) => {\n        onPointerLeave?.(e);\n        setDismissed(false);\n      }}\n      onBlur={(e) => {\n        onBlur?.(e);\n        if (!e.currentTarget.contains(e.relatedTarget as Node | null)) setDismissed(false);\n      }}\n    >\n      {anchor}\n      <span className={panel.className} style={panel.style} role=\"tooltip\" id={id} hidden={dismissed}>\n        {children}\n      </span>\n    </span>\n  );\n});\n",
      "type": "registry:component",
      "target": "components/vlak/hover-card.tsx"
    },
    {
      "path": "vlak/styles/hover-card.css",
      "content": "/* ── hover-card: generated from packages/react/src/components/hover-card.tsx ── */\n.rs-hover-card{position:relative;display:inline-block}\n.rs-hover-card-panel{position:absolute;top:calc(100% + 8px);inset-inline-start:0;z-index:var(--z-float);width:16.25rem;background-color:var(--bg);border-width:1px;border-style:solid;border-color:var(--divider);border-radius:var(--radius);box-shadow:0 8px 24px rgba(0,0,0,0.06);padding:0.875rem;font-size:0.8125rem;line-height:1.45;letter-spacing:-0.01em;color:var(--text-secondary);opacity:0;visibility:hidden;transition:var(--transition)}\n.rs-hover-card:focus-within .rs-hover-card-panel{opacity:1;visibility:visible}\n.rs-hover-card-panel::before{content:\"\";position:absolute;bottom:100%;inset-inline-start:0;inset-inline-end:0;height:0.5rem}\n@media (max-width: 640px){.rs-hover-card-panel{width:min(280px, calc(100vw - 32px));border-radius:var(--radius);box-shadow:none;font-size:0.9375rem}}\n@media (hover: hover) and (pointer: fine){.rs-hover-card:hover .rs-hover-card-panel{opacity:1;visibility:visible}}\n@media (hover: none){.rs-hover-card:active .rs-hover-card-panel{opacity:1;visibility:visible}}\n@media (prefers-reduced-motion: reduce){.rs-hover-card-panel{transition:none}}\n.rs-hover-card-open{opacity:1;visibility:visible}\n",
      "type": "registry:file",
      "target": "styles/vlak/hover-card.css"
    }
  ],
  "meta": {
    "vlak": {
      "category": "surfaces",
      "classes": [
        "rs-hover-card",
        "rs-hover-card-panel",
        "rs-hover-card-open"
      ],
      "snippet": "<span class=\"rs-hover-card\"><span tabindex=\"0\" aria-describedby=\"noord-card\">@noord</span><span class=\"rs-hover-card-panel\" role=\"tooltip\" id=\"noord-card\">Noord, a venture studio in Alkmaar. Ten portfolio companies, one design system.</span></span>",
      "cssOnly": false,
      "registryDependencies": [
        "vlak-lib",
        "tooltip"
      ],
      "aliases": [
        "Hover card",
        "Preview card",
        "Profile card",
        "Rich tooltip"
      ],
      "example": "import { HoverCard, Link } from \"@noorddev/vlak-react\";\n\n<HoverCard trigger={<Link href=\"/noord\">@noord</Link>}>\n  Noord, a venture studio in Alkmaar. Ten portfolio companies, one design system.\n</HoverCard>",
      "usage": {
        "use": [
          "A preview of the thing a link points at: a profile, a document, a place.",
          "Rich content that is not needed to use the link."
        ],
        "avoid": [
          "One-line labels; use Tooltip.",
          "Content with its own controls; use Popover."
        ]
      },
      "keyboard": [
        {
          "keys": "Tab",
          "does": "Focuses the trigger and shows the card"
        },
        {
          "keys": "Escape",
          "does": "Hides the card until the pointer leaves"
        }
      ],
      "a11y": [
        "The panel is role=\"tooltip\" and describes the trigger through aria-describedby.",
        "A focusable trigger element is used as is; anything else is wrapped in a tab stop."
      ]
    }
  }
}
