{
  "slug": "spotlight-card",
  "name": "Spotlight Card",
  "description": "Card container with cursor-following radial gradient glow effect.",
  "summary": "A dynamic glassmorphic card container that tracks pointer coordinates within its bounding box in real time. It converts viewport cursor offsets into local coordinates using the rendered-to-layout size ratio, keeping the soft 600px radial spotlight aligned under scaled ancestors. Smooth 300ms opacity fades on boundary crossing create an illuminated spotlight aesthetic across dark surfaces.",
  "category": "cards",
  "tags": [
    "card",
    "spotlight",
    "hover",
    "interactive",
    "radial-gradient",
    "glassmorphism"
  ],
  "dependencies": [
    "clsx",
    "tailwind-merge"
  ],
  "version": "1.0.0",
  "createdDate": "2026-09-10",
  "updatedDate": "2026-09-10",
  "interactive": true,
  "supportsColor": true,
  "hidden": true,
  "highlights": [
    "Client-side pointer calculation with getBoundingClientRect() coordinate tracking",
    "600px radial gradient spotlight that fades to transparent at 40%",
    "Instantaneous coordinate binding directly to CSS radial-gradient styles",
    "Smooth 300ms opacity transition during cursor boundary entry and departure",
    "backdrop-blur-md over semi-transparent white (light) or zinc-950 (dark) surfaces"
  ],
  "anatomy": [
    "<div> (Relative rounded card wrapper with border containment and hover border brightening)",
    "<div> (Spotlight Beam: pointer-events-none absolute mask rendering radial gradient)",
    "<div> (Content Layer: relative z-10 container preserving interactive child element clickability)"
  ],
  "physics": {
    "engine": "Pointer Event Matrix & Radial Shader",
    "description": "Real-time client pointer coordinate mapping calculating relative Cartesian offsets with CSS opacity decay.",
    "parameters": [
      {
        "label": "Spotlight Radius",
        "value": "600px circle"
      },
      {
        "label": "Decay Curve",
        "value": "Linear stop, transparent at 40%"
      },
      {
        "label": "Fade Duration",
        "value": "300ms transition-opacity"
      },
      {
        "label": "Coordinate Mapping",
        "value": "Cartesian offset (x, y)"
      },
      {
        "label": "Backdrop Filter",
        "value": "12px blur-md"
      }
    ]
  },
  "accessibility": {
    "aria": "The root is a plain div with no role. Pass role, aria-labelledby or aria-describedby through props when the card needs a name.",
    "reducedMotion": "No reduced-motion handling. The glow follows the pointer and fades over 300ms.",
    "keyboard": [
      {
        "key": "Tab",
        "description": "Moves focus to interactive children. The card itself is not focusable."
      }
    ]
  },
  "guidelines": {
    "recommended": [
      "Feature matrices and product highlights in modern SaaS landing pages",
      "Interactive pricing tiers and subscription comparison modules",
      "Developer portfolio project showcases and case study links"
    ],
    "bestPractices": [
      "Use subtle accent colors with low opacity (0.12 - 0.20) to prevent content unreadability",
      "Combine with high-contrast text and dark slate/zinc background surfaces",
      "Keep card interactive children inside the relative z-10 layer to prevent pointer event masking"
    ]
  },
  "props": [
    {
      "name": "spotlightColor",
      "type": "string",
      "defaultValue": "\"rgba(59, 130, 246, 0.15)\"",
      "description": "CSS RGBA, HSLA, or hex color string defining the center glow of the 600px radial light cone."
    },
    {
      "name": "className",
      "type": "string",
      "defaultValue": "undefined",
      "description": "Additional Tailwind or CSS classes applied to the root card container."
    },
    {
      "name": "children",
      "type": "React.ReactNode",
      "defaultValue": "undefined",
      "description": "Card content, headlines, telemetry, or icons rendered above the spotlight beam."
    },
    {
      "name": "onClick",
      "type": "(event: React.MouseEvent<HTMLDivElement>) => void",
      "defaultValue": "undefined",
      "description": "Optional click handler for making the spotlight card act as an interactive clickable surface."
    }
  ],
  "usage": "import { SpotlightCard } from \"@/components/ui/spotlight-card\";\n\nexport function SpotlightCardDemo() {\n  return (\n    <SpotlightCard className=\"p-6\">\n      <h3 className=\"text-lg font-semibold\">Spotlight Card</h3>\n      <p>Move the pointer over the card.</p>\n    </SpotlightCard>\n  );\n}",
  "files": [
    {
      "name": "spotlight-card.tsx",
      "path": "registry/ui/spotlight-card.tsx",
      "code": "\"use client\";\n\nimport React, { useRef, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface SpotlightCardProps extends React.HTMLAttributes<HTMLDivElement> {\n  spotlightColor?: string;\n}\n\nexport const SpotlightCard = React.forwardRef<\n  HTMLDivElement,\n  SpotlightCardProps\n>(\n  (\n    {\n      className,\n      children,\n      spotlightColor = \"rgba(59, 130, 246, 0.15)\",\n      ...props\n    },\n    ref,\n  ) => {\n    const divRef = useRef<HTMLDivElement>(null);\n    const [position, setPosition] = useState<{ x: number; y: number }>({\n      x: 0,\n      y: 0,\n    });\n    const [opacity, setOpacity] = useState(0);\n\n    const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {\n      const card = divRef.current;\n      if (!card) return;\n      const rect = card.getBoundingClientRect();\n      setPosition({\n        x: rect.width > 0\n          ? (e.clientX - rect.left) * card.offsetWidth / rect.width\n          : 0,\n        y: rect.height > 0\n          ? (e.clientY - rect.top) * card.offsetHeight / rect.height\n          : 0,\n      });\n    };\n\n    const handleMouseEnter = () => {\n      setOpacity(1);\n    };\n\n    const handleMouseLeave = () => {\n      setOpacity(0);\n    };\n\n    return (\n      <div\n        ref={(node) => {\n          divRef.current = node;\n          if (typeof ref === \"function\") {\n            ref(node);\n          } else if (ref) {\n            ref.current = node;\n          }\n        }}\n        onMouseMove={handleMouseMove}\n        onMouseEnter={handleMouseEnter}\n        onMouseLeave={handleMouseLeave}\n        className={cn(\n          \"relative overflow-hidden rounded-xl border border-zinc-200 bg-white/80 p-6 backdrop-blur-md transition-colors duration-200 hover:border-zinc-300 dark:border-zinc-800 dark:bg-zinc-950/60 dark:hover:border-zinc-700\",\n          className,\n        )}\n        {...props}\n      >\n        <div\n          className=\"pointer-events-none absolute -inset-px transition-opacity duration-300\"\n          style={{\n            opacity,\n            background: `radial-gradient(600px circle at ${position.x}px ${position.y}px, ${spotlightColor}, transparent 40%)`,\n          }}\n        />\n        <div className=\"relative z-10\">{children}</div>\n      </div>\n    );\n  },\n);\n\nSpotlightCard.displayName = \"SpotlightCard\";\n",
      "content": "\"use client\";\n\nimport React, { useRef, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface SpotlightCardProps extends React.HTMLAttributes<HTMLDivElement> {\n  spotlightColor?: string;\n}\n\nexport const SpotlightCard = React.forwardRef<\n  HTMLDivElement,\n  SpotlightCardProps\n>(\n  (\n    {\n      className,\n      children,\n      spotlightColor = \"rgba(59, 130, 246, 0.15)\",\n      ...props\n    },\n    ref,\n  ) => {\n    const divRef = useRef<HTMLDivElement>(null);\n    const [position, setPosition] = useState<{ x: number; y: number }>({\n      x: 0,\n      y: 0,\n    });\n    const [opacity, setOpacity] = useState(0);\n\n    const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {\n      const card = divRef.current;\n      if (!card) return;\n      const rect = card.getBoundingClientRect();\n      setPosition({\n        x: rect.width > 0\n          ? (e.clientX - rect.left) * card.offsetWidth / rect.width\n          : 0,\n        y: rect.height > 0\n          ? (e.clientY - rect.top) * card.offsetHeight / rect.height\n          : 0,\n      });\n    };\n\n    const handleMouseEnter = () => {\n      setOpacity(1);\n    };\n\n    const handleMouseLeave = () => {\n      setOpacity(0);\n    };\n\n    return (\n      <div\n        ref={(node) => {\n          divRef.current = node;\n          if (typeof ref === \"function\") {\n            ref(node);\n          } else if (ref) {\n            ref.current = node;\n          }\n        }}\n        onMouseMove={handleMouseMove}\n        onMouseEnter={handleMouseEnter}\n        onMouseLeave={handleMouseLeave}\n        className={cn(\n          \"relative overflow-hidden rounded-xl border border-zinc-200 bg-white/80 p-6 backdrop-blur-md transition-colors duration-200 hover:border-zinc-300 dark:border-zinc-800 dark:bg-zinc-950/60 dark:hover:border-zinc-700\",\n          className,\n        )}\n        {...props}\n      >\n        <div\n          className=\"pointer-events-none absolute -inset-px transition-opacity duration-300\"\n          style={{\n            opacity,\n            background: `radial-gradient(600px circle at ${position.x}px ${position.y}px, ${spotlightColor}, transparent 40%)`,\n          }}\n        />\n        <div className=\"relative z-10\">{children}</div>\n      </div>\n    );\n  },\n);\n\nSpotlightCard.displayName = \"SpotlightCard\";\n",
      "type": "registry:ui",
      "target": "@ui/spotlight-card.tsx"
    }
  ],
  "type": "registry:ui",
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "title": "Spotlight Card"
}
