{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "avatars-magnetic",
  "title": "Magnetic avatars",
  "description": "Styled avatars: the Magnetic set. 5 decorative avatars variations (MagneticAvatar, TiltAvatar, PopAvatar, WobbleAvatar, FollowAvatar) on the ai2 token system, powered by framer-motion, reduced-motion aware and sized sm to xl. Part of the free styled layer.",
  "dependencies": [
    "motion@^12.42.2"
  ],
  "registryDependencies": [
    "@ai2/tokens"
  ],
  "files": [
    {
      "path": "registry/ai2/styled/avatars-magnetic.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { motion, useMotionValue, useReducedMotion, useSpring } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Magnetic avatar family: 5 decorative avatars built on cursor physics and hover. Initials-based (no external image) - a consumer can put an <img> in place of the initials. Position and angle values are held with useMotionValue plus useSpring (no re-renders). Colour comes ONLY from tokens, size = the box scale. ALL pointer and hover effects are off under reduced-motion. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\nconst box: Record<StyledSize, string> = {\n  sm: \"size-8 text-xs\",\n  md: \"size-10 text-sm\",\n  lg: \"size-12 text-base\",\n  xl: \"size-16 text-lg\",\n}\n\nconst face =\n  \"flex size-full items-center justify-center rounded-full bg-secondary font-medium text-secondary-foreground select-none\"\n\ntype Props = React.ComponentProps<\"div\"> & { size?: StyledSize; initials?: string }\n\n/* Magnetic: avatar imlece dogru yayli kayar. */\nexport function MagneticAvatar({ className, size = \"md\", initials = \"AI\", ...props }: Props) {\n  const ref = React.useRef<HTMLDivElement>(null)\n  const reduce = useReducedMotion()\n  const x = useMotionValue(0)\n  const y = useMotionValue(0)\n  const springX = useSpring(x, { stiffness: 300, damping: 20 })\n  const springY = useSpring(y, { stiffness: 300, damping: 20 })\n\n  const onMove = (e: React.PointerEvent<HTMLDivElement>) => {\n    if (reduce || !ref.current) return\n    const r = ref.current.getBoundingClientRect()\n    x.set((e.clientX - (r.left + r.width / 2)) * 0.35)\n    y.set((e.clientY - (r.top + r.height / 2)) * 0.35)\n  }\n  const reset = () => {\n    x.set(0)\n    y.set(0)\n  }\n\n  return (\n    <div\n      ref={ref}\n      data-slot=\"styled-avatar\"\n      onPointerMove={onMove}\n      onPointerLeave={reset}\n      className={cn(\"relative inline-flex\", box[size], className)}\n      {...props}\n    >\n      <motion.span className={face} style={reduce ? undefined : { x: springX, y: springY }}>\n        {initials}\n      </motion.span>\n    </div>\n  )\n}\n\n/* Tilt: the avatar tilts in 3D towards the cursor (rotateX/rotateY from the pointer). */\nexport function TiltAvatar({ className, size = \"md\", initials = \"AI\", ...props }: Props) {\n  const ref = React.useRef<HTMLDivElement>(null)\n  const reduce = useReducedMotion()\n  const rx = useMotionValue(0)\n  const ry = useMotionValue(0)\n  const springRx = useSpring(rx, { stiffness: 300, damping: 20 })\n  const springRy = useSpring(ry, { stiffness: 300, damping: 20 })\n\n  const onMove = (e: React.PointerEvent<HTMLDivElement>) => {\n    if (reduce || !ref.current) return\n    const r = ref.current.getBoundingClientRect()\n    const px = (e.clientX - r.left) / r.width - 0.5\n    const py = (e.clientY - r.top) / r.height - 0.5\n    rx.set(py * -24)\n    ry.set(px * 24)\n  }\n  const reset = () => {\n    rx.set(0)\n    ry.set(0)\n  }\n\n  return (\n    <div\n      ref={ref}\n      data-slot=\"styled-avatar\"\n      onPointerMove={onMove}\n      onPointerLeave={reset}\n      className={cn(\"relative inline-flex\", box[size], className)}\n      {...props}\n    >\n      <motion.span\n        className={face}\n        style={reduce ? undefined : { rotateX: springRx, rotateY: springRy, transformPerspective: 400 }}\n      >\n        {initials}\n      </motion.span>\n    </div>\n  )\n}\n\n/* Pop: grows with a spring on hover. */\nexport function PopAvatar({ className, size = \"md\", initials = \"AI\", ...props }: Props) {\n  const reduce = useReducedMotion()\n  return (\n    <div data-slot=\"styled-avatar\" className={cn(\"relative inline-flex\", box[size], className)} {...props}>\n      <motion.span\n        className={face}\n        whileHover={reduce ? undefined : { scale: 1.15 }}\n        transition={{ type: \"spring\", stiffness: 400, damping: 14 }}\n      >\n        {initials}\n      </motion.span>\n    </div>\n  )\n}\n\n/* Wobble: does a gentle rotate wobble on hover. */\nexport function WobbleAvatar({ className, size = \"md\", initials = \"AI\", ...props }: Props) {\n  const reduce = useReducedMotion()\n  return (\n    <div data-slot=\"styled-avatar\" className={cn(\"relative inline-flex\", box[size], className)} {...props}>\n      <motion.span\n        className={face}\n        whileHover={reduce ? undefined : { rotate: [0, -9, 7, -5, 0] }}\n        transition={{ duration: 0.6, ease: \"easeInOut\" }}\n      >\n        {initials}\n      </motion.span>\n    </div>\n  )\n}\n\n/* Follow: a small token ring trailing the cursor on hover. */\nexport function FollowAvatar({ className, size = \"md\", initials = \"AI\", ...props }: Props) {\n  const ref = React.useRef<HTMLDivElement>(null)\n  const reduce = useReducedMotion()\n  const x = useMotionValue(0)\n  const y = useMotionValue(0)\n  const springX = useSpring(x, { stiffness: 150, damping: 15 })\n  const springY = useSpring(y, { stiffness: 150, damping: 15 })\n\n  const onMove = (e: React.PointerEvent<HTMLDivElement>) => {\n    if (reduce || !ref.current) return\n    const r = ref.current.getBoundingClientRect()\n    x.set((e.clientX - (r.left + r.width / 2)) * 0.6)\n    y.set((e.clientY - (r.top + r.height / 2)) * 0.6)\n  }\n  const reset = () => {\n    x.set(0)\n    y.set(0)\n  }\n\n  return (\n    <div\n      ref={ref}\n      data-slot=\"styled-avatar\"\n      onPointerMove={onMove}\n      onPointerLeave={reset}\n      className={cn(\"group relative inline-flex\", box[size], className)}\n      {...props}\n    >\n      <motion.span\n        aria-hidden=\"true\"\n        className=\"pointer-events-none absolute left-1/2 top-1/2 size-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-primary opacity-0 transition-opacity duration-(--motion-base) group-hover:opacity-100 motion-reduce:hidden\"\n        style={reduce ? undefined : { x: springX, y: springY }}\n      />\n      <span className={face}>{initials}</span>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/avatars-magnetic.tsx"
    }
  ],
  "categories": [
    "styled",
    "avatars"
  ],
  "type": "registry:component"
}