{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "social-float",
  "title": "Social Float",
  "description": "Fixed social FAB with expandable link tray.",
  "dependencies": [
    "lucide-react",
    "next",
    "motion"
  ],
  "registryDependencies": [
    "@atroui/utils"
  ],
  "files": [
    {
      "path": "registry/default/blocks/social-float.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport Link from \"next/link\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\nimport { Github, Mail, X } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst CONTENT = {\n  links: [\n    { label: \"Email\", href: \"mailto:hello@example.com\", external: false },\n    { label: \"GitHub\", href: \"https://github.com\", external: true },\n    { label: \"X / Twitter\", href: \"https://x.com\", external: true },\n    { label: \"Contact\", href: \"/contact\", external: false },\n  ] as Array<{\n    label: string\n    href: string\n    external?: boolean\n    icon?: \"mail\" | \"github\" | \"x\" | null\n  }>,\n}\n\nfunction XIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      viewBox=\"0 0 24 24\"\n      aria-hidden\n      className={className}\n      fill=\"currentColor\"\n    >\n      <path d=\"M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.744l7.727-8.835L1.254 2.25H8.08l4.259 5.672L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117L17.083 19.77z\" />\n    </svg>\n  )\n}\n\nexport function SocialFloat({\n  links = CONTENT.links,\n  className,\n}: {\n  links?: Array<{\n    label: string\n    href: string\n    external?: boolean\n    icon?: \"mail\" | \"github\" | \"x\" | null\n  }>\n  className?: string\n} = {}) {\n  const [open, setOpen] = React.useState(false)\n  const reduceMotion = useReducedMotion()\n  const rootRef = React.useRef<HTMLDivElement>(null)\n\n  React.useEffect(() => {\n    if (!open) return\n    const onKey = (e: KeyboardEvent) => {\n      if (e.key === \"Escape\") setOpen(false)\n    }\n    const onPointer = (e: MouseEvent | TouchEvent) => {\n      if (\n        rootRef.current &&\n        e.target instanceof Node &&\n        !rootRef.current.contains(e.target)\n      ) {\n        setOpen(false)\n      }\n    }\n    window.addEventListener(\"keydown\", onKey)\n    window.addEventListener(\"mousedown\", onPointer)\n    return () => {\n      window.removeEventListener(\"keydown\", onKey)\n      window.removeEventListener(\"mousedown\", onPointer)\n    }\n  }, [open])\n\n  return (\n    <div\n      ref={rootRef}\n      className={cn(\n        \"pointer-events-none fixed right-4 bottom-5 z-40 sm:right-6 sm:bottom-7\",\n        className\n      )}\n    >\n      <div className=\"pointer-events-auto flex flex-col items-end gap-2\">\n        <AnimatePresence>\n          {open ? (\n            <motion.div\n              key=\"connect-panel\"\n              role=\"dialog\"\n              aria-label=\"Connect\"\n              initial={reduceMotion ? { opacity: 0 } : { opacity: 0, y: 8 }}\n              animate={{ opacity: 1, y: 0 }}\n              exit={reduceMotion ? { opacity: 0 } : { opacity: 0, y: 6 }}\n              transition={{ duration: 0.2, ease: [0.22, 1, 0.36, 1] }}\n              className=\"w-[220px] rounded-[8px] border border-border-subtle bg-background py-1 shadow-[0_8px_24px_rgba(0,0,0,0.08)] dark:shadow-[0_8px_24px_rgba(0,0,0,0.4)]\"\n            >\n              <p className=\"px-3 pt-2 pb-1 font-mono text-[10.5px] tracking-[0.1em] text-muted-foreground/70 uppercase\">\n                Connect\n              </p>\n              <ul>\n                {links.map((item) => {\n                  const iconKind =\n                    item.icon ??\n                    (item.href.startsWith(\"mailto:\")\n                      ? \"mail\"\n                      : item.label.toLowerCase().includes(\"github\")\n                        ? \"github\"\n                        : item.label.toLowerCase().includes(\"twitter\") ||\n                            item.label.toLowerCase() === \"x\" ||\n                            item.label.toLowerCase().startsWith(\"x /\")\n                          ? \"x\"\n                          : null)\n                  const classNameRow =\n                    \"flex w-full items-center gap-2.5 px-3 py-2 font-mono text-[12px] text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n                  const inner = (\n                    <>\n                      {iconKind === \"mail\" ? (\n                        <Mail className=\"h-3.5 w-3.5 shrink-0\" />\n                      ) : iconKind === \"github\" ? (\n                        <Github className=\"h-3.5 w-3.5 shrink-0\" />\n                      ) : iconKind === \"x\" ? (\n                        <XIcon className=\"h-3.5 w-3.5 shrink-0\" />\n                      ) : (\n                        <span\n                          className=\"inline-block w-3.5 text-center text-[10px]\"\n                          aria-hidden\n                        >\n                          →\n                        </span>\n                      )}\n                      {item.label}\n                    </>\n                  )\n                  return (\n                    <li key={item.href}>\n                      {item.external || item.href.startsWith(\"mailto:\") ? (\n                        <a\n                          href={item.href}\n                          {...(item.external\n                            ? {\n                                target: \"_blank\",\n                                rel: \"noopener noreferrer\",\n                              }\n                            : {})}\n                          className={classNameRow}\n                          onClick={() => setOpen(false)}\n                        >\n                          {inner}\n                        </a>\n                      ) : (\n                        <Link\n                          href={item.href}\n                          className={classNameRow}\n                          onClick={() => setOpen(false)}\n                        >\n                          {inner}\n                        </Link>\n                      )}\n                    </li>\n                  )\n                })}\n              </ul>\n            </motion.div>\n          ) : null}\n        </AnimatePresence>\n\n        <button\n          type=\"button\"\n          aria-expanded={open}\n          aria-label={open ? \"Close connect menu\" : \"Open connect menu\"}\n          onClick={() => setOpen((v) => !v)}\n          className={cn(\n            \"inline-flex h-9 items-center gap-2 rounded-md border border-border-subtle bg-background px-3\",\n            \"font-mono text-[11.5px] text-muted-foreground\",\n            \"transition-colors hover:text-foreground\",\n            \"focus-visible:ring-2 focus-visible:ring-foreground/25 focus-visible:outline-none\"\n          )}\n        >\n          {open ? (\n            <>\n              <X className=\"h-3.5 w-3.5\" strokeWidth={1.75} />\n              close\n            </>\n          ) : (\n            \"connect\"\n          )}\n        </button>\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/blocks/social-float.tsx"
    }
  ],
  "type": "registry:block"
}