{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "cursor",
  "title": "Cursor",
  "description": "Custom cursors: ring, dot, blend, crosshair, sparkle trail, sunflower and rose.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/new-york/cursor/cursor.tsx",
      "content": "\"use client\"\n\nimport { useEffect, useRef, useState, useSyncExternalStore } from \"react\"\nimport { createPortal } from \"react-dom\"\nimport {\n  motion,\n  useMotionValue,\n  useReducedMotion,\n  useSpring,\n  useTransform,\n  useVelocity,\n  type MotionValue,\n} from \"motion/react\"\n\nexport type CursorVariant = \"ring\" | \"dot\" | \"blend\" | \"crosshair\" | \"sparkle\" | \"sunflower\" | \"rose\"\n\nexport interface CursorProps {\n  /** Cursor style. Default: \"ring\" */\n  variant?: CursorVariant\n  /** Main color (any CSS color). Default: foreground (gold for sparkle) */\n  color?: string\n  /** Size multiplier. Default: 1 */\n  size?: number\n  /** Follow spring stiffness. Higher = tighter. Default: 400 */\n  stiffness?: number\n  /** Follow spring damping. Default: 32 */\n  damping?: number\n  /** Drop particles behind the cursor (sparkle, sunflower, rose). Default: true */\n  trail?: boolean\n  /** Elements that trigger the hover state. */\n  hoverSelector?: string\n  /** Limit the cursor to this element. Defaults to the whole page. */\n  container?: HTMLElement | null\n}\n\nconst DEFAULT_HOVER = \"a, button, [role=button], input, select, textarea, label, [data-cursor-hover]\"\nconst STYLE_ID = \"mc-cursor-style\"\n\nconst noop = () => () => {}\nfunction useMounted() {\n  return useSyncExternalStore(noop, () => true, () => false)\n}\n\nfunction useFinePointer() {\n  return useSyncExternalStore(\n    (cb) => {\n      const mq = window.matchMedia(\"(pointer: fine)\")\n      mq.addEventListener(\"change\", cb)\n      return () => mq.removeEventListener(\"change\", cb)\n    },\n    () => window.matchMedia(\"(pointer: fine)\").matches,\n    () => false\n  )\n}\n\n/** Resolve any CSS color (including var()) to something canvas understands. */\nfunction resolveColor(color: string) {\n  const probe = document.createElement(\"span\")\n  probe.style.color = color\n  document.body.appendChild(probe)\n  const resolved = getComputedStyle(probe).color\n  probe.remove()\n  return resolved\n}\n\n// ------------------------------------------------------------------ particles\n\ntype Particle = {\n  x: number\n  y: number\n  vx: number\n  vy: number\n  life: number\n  max: number\n  size: number\n  rot: number\n  vr: number\n  color: string\n  kind: \"star\" | \"petal\"\n}\n\nfunction drawStar(ctx: CanvasRenderingContext2D, r: number) {\n  ctx.beginPath()\n  for (let i = 0; i < 8; i++) {\n    const a = (i * Math.PI) / 4\n    const d = i % 2 ? r * 0.28 : r\n    ctx.lineTo(Math.cos(a) * d, Math.sin(a) * d)\n  }\n  ctx.closePath()\n  ctx.fill()\n}\n\nfunction drawPetal(ctx: CanvasRenderingContext2D, r: number) {\n  ctx.beginPath()\n  ctx.moveTo(0, -r)\n  ctx.bezierCurveTo(r * 0.8, -r * 0.6, r * 0.6, r * 0.7, 0, r)\n  ctx.bezierCurveTo(-r * 0.6, r * 0.7, -r * 0.8, -r * 0.6, 0, -r)\n  ctx.fill()\n}\n\n// ------------------------------------------------------------------ shapes\n\nfunction Sunflower() {\n  const seeds = Array.from({ length: 34 }, (_, i) => {\n    const a = i * 2.39996 // golden angle\n    const r = 1.9 * Math.sqrt(i)\n    return <circle key={i} cx={Math.cos(a) * r} cy={Math.sin(a) * r} r={1.3} fill=\"#8A5A2B\" />\n  })\n  return (\n    <svg viewBox=\"-50 -50 100 100\" className=\"size-full overflow-visible drop-shadow-[0_2px_4px_rgb(0_0_0/0.35)]\">\n      {Array.from({ length: 16 }, (_, i) => (\n        <ellipse key={`a${i}`} cx={0} cy={-30} rx={7.5} ry={17} fill=\"#F4A900\" transform={`rotate(${i * 22.5})`} />\n      ))}\n      {Array.from({ length: 16 }, (_, i) => (\n        <ellipse key={`b${i}`} cx={0} cy={-27} rx={6.5} ry={15} fill=\"#FFCB2E\" transform={`rotate(${i * 22.5 + 11.25})`} />\n      ))}\n      <circle r={17} fill=\"#5A3312\" />\n      <circle r={13} fill=\"#3D210A\" />\n      {seeds}\n    </svg>\n  )\n}\n\nconst ROSE_PETAL = \"M0 0 C -19 -8 -21 -33 0 -37 C 21 -33 19 -8 0 0 Z\"\n\nfunction Rose() {\n  return (\n    <svg viewBox=\"-50 -50 100 100\" className=\"size-full overflow-visible drop-shadow-[0_2px_4px_rgb(0_0_0/0.35)]\">\n      <path d=\"M-6 16 C -30 22 -40 38 -40 38 C -22 42 -8 32 -6 16 Z\" fill=\"#2F7D3A\" />\n      <path d=\"M6 16 C 30 22 40 38 40 38 C 22 42 8 32 6 16 Z\" fill=\"#3E9A4A\" />\n      {Array.from({ length: 5 }, (_, i) => (\n        <path key={`o${i}`} d={ROSE_PETAL} fill=\"#9E0F2A\" transform={`rotate(${i * 72})`} />\n      ))}\n      {Array.from({ length: 5 }, (_, i) => (\n        <path key={`m${i}`} d={ROSE_PETAL} fill=\"#C81D3A\" transform={`rotate(${i * 72 + 36}) scale(0.72)`} />\n      ))}\n      <circle r={12} fill=\"#E03A54\" />\n      <path\n        d=\"M0 0 m-2 0 a2 2 0 1 1 4 0 a5 5 0 1 1 -10 0 a8 8 0 1 1 16 0\"\n        fill=\"none\"\n        stroke=\"#8E0E24\"\n        strokeWidth={1.6}\n        strokeLinecap=\"round\"\n      />\n    </svg>\n  )\n}\n\n// ------------------------------------------------------------------ component\n\nexport function Cursor({\n  variant = \"ring\",\n  color,\n  size = 1,\n  stiffness = 400,\n  damping = 32,\n  trail = true,\n  hoverSelector = DEFAULT_HOVER,\n  container,\n}: CursorProps) {\n  const mounted = useMounted()\n  const fine = useFinePointer()\n  const reduced = useReducedMotion()\n\n  const [visible, setVisible] = useState(false)\n  const [hover, setHover] = useState(false)\n  const [pressed, setPressed] = useState(false)\n  const [coords, setCoords] = useState({ x: 0, y: 0 })\n\n  const canvasRef = useRef<HTMLCanvasElement>(null)\n\n  // Raw pointer and a sprung follower\n  const x = useMotionValue(-100)\n  const y = useMotionValue(-100)\n  const spring = reduced ? { stiffness: 2000, damping: 100 } : { stiffness, damping, mass: 0.5 }\n  const sx = useSpring(x, spring)\n  const sy = useSpring(y, spring)\n\n  // Flower motion: sunflower rolls with horizontal travel, rose tilts with speed\n  const roll = useTransform(sx, (v) => v * 0.6)\n  const vx = useVelocity(sx)\n  const tilt = useSpring(useTransform(vx, [-2500, 0, 2500], [-40, 0, 40], { clamp: true }), {\n    stiffness: 200,\n    damping: 18,\n  })\n\n  const accent = color ?? (variant === \"sparkle\" ? \"#FFD76A\" : \"var(--foreground)\")\n  const particles = trail && !reduced && (variant === \"sparkle\" || variant === \"sunflower\" || variant === \"rose\")\n\n  useEffect(() => {\n    if (!mounted || !fine) return\n    const scope: HTMLElement = container ?? document.documentElement\n\n    // Hide the native cursor inside the scope\n    if (!document.getElementById(STYLE_ID)) {\n      const style = document.createElement(\"style\")\n      style.id = STYLE_ID\n      style.textContent = \"[data-mc-cursor],[data-mc-cursor] *{cursor:none!important}\"\n      document.head.appendChild(style)\n    }\n    scope.setAttribute(\"data-mc-cursor\", \"\")\n\n    // Particle system\n    const canvas = canvasRef.current\n    const ctx = canvas?.getContext(\"2d\")\n    const list: Particle[] = []\n    let raf = 0\n    let last: { x: number; y: number } | null = null\n    const petalColors =\n      variant === \"rose\" ? [\"#9E0F2A\", \"#C81D3A\", \"#E03A54\"] : [\"#F4A900\", \"#FFCB2E\", \"#FFD95E\"]\n    const starColor = resolveColor(accent)\n\n    const resize = () => {\n      if (!canvas || !ctx) return\n      const dpr = Math.min(window.devicePixelRatio || 1, 2)\n      canvas.width = innerWidth * dpr\n      canvas.height = innerHeight * dpr\n      ctx.setTransform(dpr, 0, 0, dpr, 0, 0)\n    }\n\n    const loop = () => {\n      if (!ctx) return\n      ctx.clearRect(0, 0, innerWidth, innerHeight)\n      for (let i = list.length - 1; i >= 0; i--) {\n        const p = list[i]\n        p.life++\n        if (p.life >= p.max) {\n          list.splice(i, 1)\n          continue\n        }\n        if (p.kind === \"petal\") {\n          p.vy += 0.06 // gravity\n          p.vx += Math.sin(p.life / 9) * 0.05 // flutter\n        } else {\n          p.vx *= 0.94\n          p.vy *= 0.94\n        }\n        p.x += p.vx\n        p.y += p.vy\n        p.rot += p.vr\n        const t = p.life / p.max\n        ctx.save()\n        ctx.globalAlpha = p.kind === \"star\" ? 1 - t : Math.min(1, (1 - t) * 1.6)\n        ctx.fillStyle = p.color\n        ctx.translate(p.x, p.y)\n        ctx.rotate(p.rot)\n        if (p.kind === \"star\") drawStar(ctx, p.size * (1 - t * 0.6))\n        else drawPetal(ctx, p.size)\n        ctx.restore()\n      }\n      raf = list.length ? requestAnimationFrame(loop) : 0\n    }\n\n    const spawn = (px: number, py: number) => {\n      if (!particles || !last) return\n      const dist = Math.hypot(px - last.x, py - last.y)\n      const every = variant === \"sparkle\" ? 14 : 38\n      if (dist < every) return\n      last = { x: px, y: py }\n      const petal = variant !== \"sparkle\"\n      list.push({\n        x: px + (Math.random() - 0.5) * 10 * size,\n        y: py + (Math.random() - 0.5) * 10 * size,\n        vx: (Math.random() - 0.5) * (petal ? 1.2 : 1.6),\n        vy: petal ? -Math.random() * 1.2 : (Math.random() - 0.5) * 1.6,\n        life: 0,\n        max: petal ? 90 + Math.random() * 40 : 36 + Math.random() * 20,\n        size: (petal ? 4 + Math.random() * 3 : 3 + Math.random() * 4) * size,\n        rot: Math.random() * Math.PI,\n        vr: (Math.random() - 0.5) * (petal ? 0.12 : 0.2),\n        color: petal ? petalColors[Math.floor(Math.random() * petalColors.length)] : starColor,\n        kind: petal ? \"petal\" : \"star\",\n      })\n      if (!raf) raf = requestAnimationFrame(loop)\n    }\n\n    const onMove = (e: PointerEvent) => {\n      if (e.pointerType !== \"mouse\" && e.pointerType !== \"pen\") return\n      x.set(e.clientX)\n      y.set(e.clientY)\n      if (variant === \"crosshair\") setCoords({ x: Math.round(e.clientX), y: Math.round(e.clientY) })\n      setVisible(true)\n      setHover(!!(e.target as Element | null)?.closest?.(hoverSelector))\n      if (!last) last = { x: e.clientX, y: e.clientY }\n      spawn(e.clientX, e.clientY)\n    }\n    const onLeave = () => {\n      setVisible(false)\n      last = null\n    }\n    const onDown = () => setPressed(true)\n    const onUp = () => setPressed(false)\n\n    resize()\n    window.addEventListener(\"resize\", resize)\n    const target: HTMLElement | Window = container ?? window\n    target.addEventListener(\"pointermove\", onMove as EventListener)\n    target.addEventListener(\"pointerdown\", onDown)\n    window.addEventListener(\"pointerup\", onUp)\n    if (container) container.addEventListener(\"pointerleave\", onLeave)\n    else document.documentElement.addEventListener(\"mouseleave\", onLeave)\n\n    return () => {\n      cancelAnimationFrame(raf)\n      scope.removeAttribute(\"data-mc-cursor\")\n      window.removeEventListener(\"resize\", resize)\n      target.removeEventListener(\"pointermove\", onMove as EventListener)\n      target.removeEventListener(\"pointerdown\", onDown)\n      window.removeEventListener(\"pointerup\", onUp)\n      if (container) container.removeEventListener(\"pointerleave\", onLeave)\n      else document.documentElement.removeEventListener(\"mouseleave\", onLeave)\n    }\n  }, [mounted, fine, container, variant, hoverSelector, particles, accent, size, x, y])\n\n  if (!mounted || !fine) return null\n\n  const scale = pressed ? 0.8 : 1\n  const fade = { opacity: visible ? 1 : 0, transition: \"opacity 150ms\" }\n\n  return createPortal(\n    <div aria-hidden=\"true\" className=\"pointer-events-none fixed inset-0 z-[9999]\" style={fade}>\n      {particles && <canvas ref={canvasRef} className=\"absolute inset-0 size-full\" />}\n      <Shape\n        variant={variant}\n        x={x}\n        y={y}\n        sx={sx}\n        sy={sy}\n        roll={roll}\n        tilt={tilt}\n        color={accent}\n        size={size}\n        hover={hover}\n        scale={scale}\n        coords={coords}\n      />\n    </div>,\n    document.body\n  )\n}\n\nfunction Shape({ variant, x, y, sx, sy, roll, tilt, color, size, hover, scale, coords }: {\n  variant: CursorVariant\n  x: MotionValue<number>\n  y: MotionValue<number>\n  sx: MotionValue<number>\n  sy: MotionValue<number>\n  roll: MotionValue<number>\n  tilt: MotionValue<number>\n  color: string\n  size: number\n  hover: boolean\n  scale: number\n  coords: { x: number; y: number }\n}) {\n  const spring = { type: \"spring\", stiffness: 500, damping: 30 } as const\n  const center = \"absolute left-0 top-0 -translate-x-1/2 -translate-y-1/2\"\n\n  switch (variant) {\n    case \"ring\":\n      return (\n        <>\n          <motion.div\n            className={`${center} rounded-full border-[1.5px]`}\n            style={{ x: sx, y: sy, width: 34 * size, height: 34 * size, borderColor: color }}\n            animate={{ scale: (hover ? 1.6 : 1) * scale, opacity: hover ? 0.6 : 1 }}\n            transition={spring}\n          />\n          <motion.div\n            className={`${center} rounded-full`}\n            style={{ x, y, width: 6 * size, height: 6 * size, background: color }}\n            animate={{ scale: hover ? 0 : 1 }}\n            transition={spring}\n          />\n        </>\n      )\n\n    case \"dot\":\n      return (\n        <motion.div\n          className={`${center} rounded-full`}\n          style={{ x: sx, y: sy, width: 12 * size, height: 12 * size, background: color }}\n          animate={{ scale: (hover ? 3.2 : 1) * scale, opacity: hover ? 0.35 : 1 }}\n          transition={spring}\n        />\n      )\n\n    case \"blend\":\n      return (\n        <motion.div\n          className={`${center} rounded-full bg-white mix-blend-difference`}\n          style={{ x: sx, y: sy, width: 30 * size, height: 30 * size }}\n          animate={{ scale: (hover ? 2.6 : 1) * scale }}\n          transition={spring}\n        />\n      )\n\n    case \"crosshair\":\n      return (\n        <motion.div className={center} style={{ x: sx, y: sy }}>\n          <motion.div\n            className=\"relative\"\n            style={{ width: 30 * size, height: 30 * size }}\n            animate={{ rotate: hover ? 45 : 0, scale }}\n            transition={spring}\n          >\n            <span className=\"absolute left-0 top-1/2 h-px w-[38%] -translate-y-1/2\" style={{ background: color }} />\n            <span className=\"absolute right-0 top-1/2 h-px w-[38%] -translate-y-1/2\" style={{ background: color }} />\n            <span className=\"absolute left-1/2 top-0 h-[38%] w-px -translate-x-1/2\" style={{ background: color }} />\n            <span className=\"absolute bottom-0 left-1/2 h-[38%] w-px -translate-x-1/2\" style={{ background: color }} />\n          </motion.div>\n          <span\n            className=\"absolute left-full top-full ml-1 mt-1 whitespace-nowrap font-mono text-[10px] tabular-nums opacity-70\"\n            style={{ color }}\n          >\n            {coords.x}, {coords.y}\n          </span>\n        </motion.div>\n      )\n\n    case \"sparkle\":\n      return (\n        <motion.div className={center} style={{ x: sx, y: sy, width: 22 * size, height: 22 * size }}>\n          <motion.svg\n            viewBox=\"-10 -10 20 20\"\n            className=\"size-full\"\n            animate={{ rotate: 360, scale: (hover ? 1.5 : 1) * scale }}\n            transition={{ rotate: { duration: 4, repeat: Infinity, ease: \"linear\" }, scale: spring }}\n          >\n            <path d=\"M0 -10 L2.4 -2.4 L10 0 L2.4 2.4 L0 10 L-2.4 2.4 L-10 0 L-2.4 -2.4 Z\" fill={color} />\n          </motion.svg>\n        </motion.div>\n      )\n\n    case \"sunflower\":\n      return (\n        <motion.div className={center} style={{ x: sx, y: sy, width: 40 * size, height: 40 * size, rotate: roll }}>\n          <motion.div className=\"size-full\" animate={{ scale: (hover ? 1.35 : 1) * scale }} transition={spring}>\n            <Sunflower />\n          </motion.div>\n        </motion.div>\n      )\n\n    case \"rose\":\n      return (\n        <motion.div className={center} style={{ x: sx, y: sy, width: 40 * size, height: 40 * size, rotate: tilt }}>\n          <motion.div className=\"size-full\" animate={{ scale: (hover ? 1.35 : 1) * scale }} transition={spring}>\n            <Rose />\n          </motion.div>\n        </motion.div>\n      )\n  }\n}\n",
      "type": "registry:component"
    }
  ],
  "docs": "Docs, live playground and every prop: https://trytweenly.vercel.app/docs/components/cursor",
  "type": "registry:component"
}