Skip to content

Commit

Permalink
feat: added spotlight effect on border in magic card
Browse files Browse the repository at this point in the history
  • Loading branch information
itsarghyadas committed Dec 27, 2024
1 parent 8da3acd commit c121a12
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 14 deletions.
16 changes: 8 additions & 8 deletions content/docs/components/magic-card.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ npx shadcn@latest add "https://magicui.design/r/magic-card"

### MagicCard

| Prop name | Type | Default | Description |
| -------------------- | --------------- | --------- | ------------------------------------------- |
| children | React.ReactNode | - | The content to be rendered inside the card |
| className | string | - | Additional CSS classes to apply to the card |
| gradientSize | number | 200 | Size of the gradient effect |
| gradientColor | string | "#262626" | Color of the gradient effect |
| gradientOpacity | number | - | Opacity of the gradient effect |
| gradientTransparency | number | 80 | Transparency level of the gradient effect |
| Prop name | Type | Default | Description |
| --------------- | --------------- | --------- | ------------------------------------------- |
| children | React.ReactNode | - | The content to be rendered inside the card |
| className | string | - | Additional CSS classes to apply to the card |
| gradientSize | number | 200 | Size of the gradient effect |
| gradientColor | string | "#262626" | Color of the gradient effect |
| gradientOpacity | number | - | Opacity of the gradient effect |
| gradientColors | string[] | - | Colors of the gradient effect |

## Credits

Expand Down
2 changes: 1 addition & 1 deletion public/r/styles/default/magic-card.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"files": [
{
"path": "magicui/magic-card.tsx",
"content": "\"use client\";\n\nimport React, { useCallback, useEffect, useRef } from \"react\";\nimport { motion, useMotionTemplate, useMotionValue } from \"framer-motion\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport interface MagicCardProps extends React.HTMLAttributes<HTMLDivElement> {\n gradientSize?: number;\n gradientColor?: string;\n gradientOpacity?: number;\n}\n\nexport function MagicCard({\n children,\n className,\n gradientSize = 200,\n gradientColor = \"#262626\",\n gradientOpacity = 0.8,\n}: MagicCardProps) {\n const cardRef = useRef<HTMLDivElement>(null);\n const mouseX = useMotionValue(-gradientSize);\n const mouseY = useMotionValue(-gradientSize);\n\n const handleMouseMove = useCallback(\n (e: MouseEvent) => {\n if (cardRef.current) {\n const { left, top } = cardRef.current.getBoundingClientRect();\n const clientX = e.clientX;\n const clientY = e.clientY;\n mouseX.set(clientX - left);\n mouseY.set(clientY - top);\n }\n },\n [mouseX, mouseY],\n );\n\n const handleMouseOut = useCallback(\n (e: MouseEvent) => {\n if (!e.relatedTarget) {\n document.removeEventListener(\"mousemove\", handleMouseMove);\n mouseX.set(-gradientSize);\n mouseY.set(-gradientSize);\n }\n },\n [handleMouseMove, mouseX, gradientSize, mouseY],\n );\n\n const handleMouseEnter = useCallback(() => {\n document.addEventListener(\"mousemove\", handleMouseMove);\n mouseX.set(-gradientSize);\n mouseY.set(-gradientSize);\n }, [handleMouseMove, mouseX, gradientSize, mouseY]);\n\n useEffect(() => {\n document.addEventListener(\"mousemove\", handleMouseMove);\n document.addEventListener(\"mouseout\", handleMouseOut);\n document.addEventListener(\"mouseenter\", handleMouseEnter);\n\n return () => {\n document.removeEventListener(\"mousemove\", handleMouseMove);\n document.removeEventListener(\"mouseout\", handleMouseOut);\n document.removeEventListener(\"mouseenter\", handleMouseEnter);\n };\n }, [handleMouseEnter, handleMouseMove, handleMouseOut]);\n\n useEffect(() => {\n mouseX.set(-gradientSize);\n mouseY.set(-gradientSize);\n }, [gradientSize, mouseX, mouseY]);\n\n return (\n <div\n ref={cardRef}\n className={cn(\n \"group relative flex size-full overflow-hidden rounded-xl bg-neutral-100 dark:bg-neutral-900 border text-black dark:text-white\",\n className,\n )}\n >\n <div className=\"relative z-10\">{children}</div>\n <motion.div\n className=\"pointer-events-none absolute -inset-px rounded-xl opacity-0 transition-opacity duration-300 group-hover:opacity-100\"\n style={{\n background: useMotionTemplate`\n radial-gradient(${gradientSize}px circle at ${mouseX}px ${mouseY}px, ${gradientColor}, transparent 100%)\n `,\n opacity: gradientOpacity,\n }}\n />\n </div>\n );\n}\n",
"content": "\"use client\";\n\nimport React, { useCallback, useEffect, useRef } from \"react\";\nimport { motion, useMotionTemplate, useMotionValue } from \"framer-motion\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport interface MagicCardProps extends React.HTMLAttributes<HTMLDivElement> {\n gradientSize?: number;\n gradientColor?: string;\n gradientOpacity?: number;\n gradientColors?: [string, string, string];\n}\n\nexport function MagicCard({\n children,\n className,\n gradientSize = 200,\n gradientColor = \"#262626\",\n gradientOpacity = 0.8,\n gradientColors = [\"#FF0080\", \"#4096FF\", \"#78FF78\"],\n}: MagicCardProps) {\n const cardRef = useRef<HTMLDivElement>(null);\n const mouseX = useMotionValue(-gradientSize);\n const mouseY = useMotionValue(-gradientSize);\n\n const handleMouseMove = useCallback(\n (e: MouseEvent) => {\n if (cardRef.current) {\n const { left, top } = cardRef.current.getBoundingClientRect();\n const clientX = e.clientX;\n const clientY = e.clientY;\n mouseX.set(clientX - left);\n mouseY.set(clientY - top);\n }\n },\n [mouseX, mouseY]\n );\n\n const handleMouseOut = useCallback(\n (e: MouseEvent) => {\n if (!e.relatedTarget) {\n document.removeEventListener(\"mousemove\", handleMouseMove);\n mouseX.set(-gradientSize);\n mouseY.set(-gradientSize);\n }\n },\n [handleMouseMove, mouseX, gradientSize, mouseY]\n );\n\n const handleMouseEnter = useCallback(() => {\n document.addEventListener(\"mousemove\", handleMouseMove);\n mouseX.set(-gradientSize);\n mouseY.set(-gradientSize);\n }, [handleMouseMove, mouseX, gradientSize, mouseY]);\n\n useEffect(() => {\n document.addEventListener(\"mousemove\", handleMouseMove);\n document.addEventListener(\"mouseout\", handleMouseOut);\n document.addEventListener(\"mouseenter\", handleMouseEnter);\n\n return () => {\n document.removeEventListener(\"mousemove\", handleMouseMove);\n document.removeEventListener(\"mouseout\", handleMouseOut);\n document.removeEventListener(\"mouseenter\", handleMouseEnter);\n };\n }, [handleMouseEnter, handleMouseMove, handleMouseOut]);\n\n useEffect(() => {\n mouseX.set(-gradientSize);\n mouseY.set(-gradientSize);\n }, [gradientSize, mouseX, mouseY]);\n\n return (\n <div\n ref={cardRef}\n className={cn(\n \"group relative flex size-full rounded-xl backdrop-blur-lg\",\n className\n )}\n >\n <div className=\"absolute inset-0.5 z-10 rounded-xl bg-neutral-100 dark:bg-neutral-900\" />\n <div className=\"relative z-30\">{children}</div>\n <motion.div\n className=\"pointer-events-none absolute inset-px z-10 rounded-xl opacity-0 transition-opacity duration-300 group-hover:opacity-100\"\n style={{\n background: useMotionTemplate`\n radial-gradient(${gradientSize}px circle at ${mouseX}px ${mouseY}px, ${gradientColor}, transparent 100%)\n `,\n opacity: gradientOpacity,\n }}\n />\n <motion.div\n className=\"pointer-events-none absolute inset-px rounded-xl duration-300 group-hover:opacity-100\"\n style={{\n background: useMotionTemplate`\n radial-gradient(${gradientSize}px circle at ${mouseX}px ${mouseY}px,\n ${gradientColors[0]}, \n ${gradientColors[1]}, \n ${gradientColors[2]}, \n transparent 100%\n )\n `,\n opacity: 1,\n }}\n />\n </div>\n );\n}\n",
"type": "registry:ui",
"target": ""
}
Expand Down
6 changes: 4 additions & 2 deletions registry/default/example/magic-card-demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ export default function MagicCardDemo() {
}
>
<MagicCard
className="cursor-pointer flex-col items-center justify-center shadow-2xl whitespace-nowrap text-4xl"
className="cursor-pointer flex-col items-center justify-center whitespace-nowrap text-4xl shadow-2xl"
gradientColor={theme === "dark" ? "#262626" : "#D9D9D955"}
gradientColors={["#00ffff", "#0088ff", "#0033cc"]}
>
Magic
</MagicCard>
<MagicCard
className="cursor-pointer flex-col items-center justify-center shadow-2xl whitespace-nowrap text-4xl"
className="cursor-pointer flex-col items-center justify-center whitespace-nowrap text-4xl shadow-2xl"
gradientColor={theme === "dark" ? "#262626" : "#D9D9D955"}
gradientColors={["#00ffff", "#0088ff", "#0033cc"]}
>
Card
</MagicCard>
Expand Down
23 changes: 20 additions & 3 deletions registry/default/magicui/magic-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface MagicCardProps extends React.HTMLAttributes<HTMLDivElement> {
gradientSize?: number;
gradientColor?: string;
gradientOpacity?: number;
gradientColors?: [string, string, string];
}

export function MagicCard({
Expand All @@ -17,6 +18,7 @@ export function MagicCard({
gradientSize = 200,
gradientColor = "#262626",
gradientOpacity = 0.8,
gradientColors = ["#FF0080", "#4096FF", "#78FF78"],
}: MagicCardProps) {
const cardRef = useRef<HTMLDivElement>(null);
const mouseX = useMotionValue(-gradientSize);
Expand Down Expand Up @@ -73,20 +75,35 @@ export function MagicCard({
<div
ref={cardRef}
className={cn(
"group relative flex size-full overflow-hidden rounded-xl bg-neutral-100 dark:bg-neutral-900 border text-black dark:text-white",
"group relative flex size-full rounded-xl backdrop-blur-lg",
className,
)}
>
<div className="relative z-10">{children}</div>
<div className="absolute inset-0.5 z-10 rounded-xl bg-neutral-100 dark:bg-neutral-900" />
<div className="relative z-30">{children}</div>
<motion.div
className="pointer-events-none absolute -inset-px rounded-xl opacity-0 transition-opacity duration-300 group-hover:opacity-100"
className="pointer-events-none absolute inset-px z-10 rounded-xl opacity-0 transition-opacity duration-300 group-hover:opacity-100"
style={{
background: useMotionTemplate`
radial-gradient(${gradientSize}px circle at ${mouseX}px ${mouseY}px, ${gradientColor}, transparent 100%)
`,
opacity: gradientOpacity,
}}
/>
<motion.div
className="pointer-events-none absolute inset-px rounded-xl duration-300 group-hover:opacity-100"
style={{
background: useMotionTemplate`
radial-gradient(${gradientSize}px circle at ${mouseX}px ${mouseY}px,
${gradientColors[0]},
${gradientColors[1]},
${gradientColors[2]},
transparent 100%
)
`,
opacity: 1,
}}
/>
</div>
);
}

0 comments on commit c121a12

Please sign in to comment.