{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "center-with-image-hero", "type": "registry:block", "title": "Center with image hero", "description": "A centered layout with an image and content", "dependencies": [ "next-themes", "motion" ], "registryDependencies": [ "utils", "button" ], "files": [ { "path": "registry/default/blocks/hero-sections/center-with-image-hero.tsx", "content": "import { Particles } from '@/registry/default/ui/particles'\r\nimport { buttonVariants } from '@/registry/default/ui/button'\r\nimport { cn } from '@/lib/utils'\r\nimport { ArrowRightIcon } from 'lucide-react'\r\nimport { motion as m } from 'motion/react'\r\n\r\nexport default function Hero() {\r\n return (\r\n
\r\n
\r\n \r\n \r\n 🎉\r\n \r\n New Feature Alert:\r\n \r\n Beta Access Now Open!\r\n \r\n \r\n \r\n \r\n Re-usable Page Blocks and Sections\r\n \r\n \r\n Modular, responsive hero sections built with{' '}\r\n \r\n shadcn/ui\r\n {' '}\r\n and{' '}\r\n \r\n Tailwind CSS\r\n {' '}\r\n — featuring the{' '}\r\n \r\n Rainbow Button\r\n {' '}\r\n and{' '}\r\n \r\n Particles\r\n {' '}\r\n sourced from{' '}\r\n \r\n Magic UI\r\n \r\n .\r\n \r\n \r\n \r\n Sign-up now\r\n — it's free\r\n \r\n \r\n
\r\n\r\n
\r\n \r\n
\r\n \r\n
\r\n \r\n
\r\n \r\n
\r\n\r\n \r\n
\r\n )\r\n}\r\n", "type": "registry:component", "target": "components/hero.tsx" }, { "path": "registry/default/ui/particles.tsx", "content": "'use client'\r\n\r\nimport { cn } from '@/lib/utils'\r\nimport { useTheme } from 'next-themes'\r\nimport React, {\r\n ComponentPropsWithoutRef,\r\n useEffect,\r\n useRef,\r\n useState,\r\n} from 'react'\r\n\r\ninterface MousePosition {\r\n x: number\r\n y: number\r\n}\r\n\r\nfunction MousePosition(): MousePosition {\r\n const [mousePosition, setMousePosition] = useState({\r\n x: 0,\r\n y: 0,\r\n })\r\n\r\n useEffect(() => {\r\n const handleMouseMove = (event: MouseEvent) => {\r\n setMousePosition({ x: event.clientX, y: event.clientY })\r\n }\r\n\r\n window.addEventListener('mousemove', handleMouseMove)\r\n\r\n return () => {\r\n window.removeEventListener('mousemove', handleMouseMove)\r\n }\r\n }, [])\r\n\r\n return mousePosition\r\n}\r\n\r\ninterface ParticlesProps extends ComponentPropsWithoutRef<'div'> {\r\n className?: string\r\n quantity?: number\r\n staticity?: number\r\n ease?: number\r\n size?: number\r\n refresh?: boolean\r\n color?: string\r\n vx?: number\r\n vy?: number\r\n accordingToTheme?: boolean\r\n}\r\n\r\nfunction hexToRgb(hex: string): number[] {\r\n hex = hex.replace('#', '')\r\n\r\n if (hex.length === 3) {\r\n hex = hex\r\n .split('')\r\n .map((char) => char + char)\r\n .join('')\r\n }\r\n\r\n const hexInt = parseInt(hex, 16)\r\n const red = (hexInt >> 16) & 255\r\n const green = (hexInt >> 8) & 255\r\n const blue = hexInt & 255\r\n return [red, green, blue]\r\n}\r\n\r\ntype Circle = {\r\n x: number\r\n y: number\r\n translateX: number\r\n translateY: number\r\n size: number\r\n alpha: number\r\n targetAlpha: number\r\n dx: number\r\n dy: number\r\n magnetism: number\r\n}\r\n\r\nexport const Particles: React.FC = ({\r\n className = '',\r\n quantity = 100,\r\n staticity = 50,\r\n ease = 50,\r\n size = 0.4,\r\n refresh = false,\r\n color = '#ffffff',\r\n accordingToTheme = false,\r\n vx = 0,\r\n vy = 0,\r\n ...props\r\n}) => {\r\n const canvasRef = useRef(null)\r\n const canvasContainerRef = useRef(null)\r\n const context = useRef(null)\r\n const circles = useRef([])\r\n const mousePosition = MousePosition()\r\n const mouse = useRef<{ x: number; y: number }>({ x: 0, y: 0 })\r\n const canvasSize = useRef<{ w: number; h: number }>({ w: 0, h: 0 })\r\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio : 1\r\n const rafID = useRef(null)\r\n const resizeTimeout = useRef(null)\r\n\r\n const { resolvedTheme } = useTheme()\r\n const [c, setC] = useState(color)\r\n\r\n useEffect(() => {\r\n setC(resolvedTheme === 'dark' ? '#ffffff' : '#000000')\r\n }, [resolvedTheme])\r\n\r\n useEffect(() => {\r\n if (canvasRef.current) {\r\n context.current = canvasRef.current.getContext('2d')\r\n }\r\n initCanvas()\r\n animate()\r\n\r\n const handleResize = () => {\r\n if (resizeTimeout.current) {\r\n clearTimeout(resizeTimeout.current)\r\n }\r\n resizeTimeout.current = setTimeout(() => {\r\n initCanvas()\r\n }, 200)\r\n }\r\n\r\n window.addEventListener('resize', handleResize)\r\n\r\n return () => {\r\n if (rafID.current != null) {\r\n window.cancelAnimationFrame(rafID.current)\r\n }\r\n if (resizeTimeout.current) {\r\n clearTimeout(resizeTimeout.current)\r\n }\r\n window.removeEventListener('resize', handleResize)\r\n }\r\n }, [color, c])\r\n\r\n useEffect(() => {\r\n onMouseMove()\r\n }, [mousePosition.x, mousePosition.y])\r\n\r\n useEffect(() => {\r\n initCanvas()\r\n }, [refresh])\r\n\r\n const initCanvas = () => {\r\n resizeCanvas()\r\n drawParticles()\r\n }\r\n\r\n const onMouseMove = () => {\r\n if (canvasRef.current) {\r\n const rect = canvasRef.current.getBoundingClientRect()\r\n const { w, h } = canvasSize.current\r\n const x = mousePosition.x - rect.left - w / 2\r\n const y = mousePosition.y - rect.top - h / 2\r\n const inside = x < w / 2 && x > -w / 2 && y < h / 2 && y > -h / 2\r\n if (inside) {\r\n mouse.current.x = x\r\n mouse.current.y = y\r\n }\r\n }\r\n }\r\n\r\n const resizeCanvas = () => {\r\n if (\r\n canvasContainerRef.current &&\r\n canvasRef.current &&\r\n context.current\r\n ) {\r\n canvasSize.current.w = canvasContainerRef.current.offsetWidth\r\n canvasSize.current.h = canvasContainerRef.current.offsetHeight\r\n\r\n canvasRef.current.width = canvasSize.current.w * dpr\r\n canvasRef.current.height = canvasSize.current.h * dpr\r\n canvasRef.current.style.width = `${canvasSize.current.w}px`\r\n canvasRef.current.style.height = `${canvasSize.current.h}px`\r\n context.current.scale(dpr, dpr)\r\n\r\n // Clear existing particles and create new ones with exact quantity\r\n circles.current = []\r\n for (let i = 0; i < quantity; i++) {\r\n const circle = circleParams()\r\n drawCircle(circle)\r\n }\r\n }\r\n }\r\n\r\n const circleParams = (): Circle => {\r\n const x = Math.floor(Math.random() * canvasSize.current.w)\r\n const y = Math.floor(Math.random() * canvasSize.current.h)\r\n const translateX = 0\r\n const translateY = 0\r\n const pSize = Math.floor(Math.random() * 2) + size\r\n const alpha = 0\r\n const targetAlpha = parseFloat((Math.random() * 0.6 + 0.1).toFixed(1))\r\n const dx = (Math.random() - 0.5) * 0.1\r\n const dy = (Math.random() - 0.5) * 0.1\r\n const magnetism = 0.1 + Math.random() * 4\r\n return {\r\n x,\r\n y,\r\n translateX,\r\n translateY,\r\n size: pSize,\r\n alpha,\r\n targetAlpha,\r\n dx,\r\n dy,\r\n magnetism,\r\n }\r\n }\r\n\r\n const rgb = hexToRgb(accordingToTheme ? c : color)\r\n\r\n const drawCircle = (circle: Circle, update = false) => {\r\n if (context.current) {\r\n const { x, y, translateX, translateY, size, alpha } = circle\r\n context.current.translate(translateX, translateY)\r\n context.current.beginPath()\r\n context.current.arc(x, y, size, 0, 2 * Math.PI)\r\n context.current.fillStyle = `rgba(${rgb.join(', ')}, ${alpha})`\r\n context.current.fill()\r\n context.current.setTransform(dpr, 0, 0, dpr, 0, 0)\r\n\r\n if (!update) {\r\n circles.current.push(circle)\r\n }\r\n }\r\n }\r\n\r\n const clearContext = () => {\r\n if (context.current) {\r\n context.current.clearRect(\r\n 0,\r\n 0,\r\n canvasSize.current.w,\r\n canvasSize.current.h\r\n )\r\n }\r\n }\r\n\r\n const drawParticles = () => {\r\n clearContext()\r\n const particleCount = quantity\r\n for (let i = 0; i < particleCount; i++) {\r\n const circle = circleParams()\r\n drawCircle(circle)\r\n }\r\n }\r\n\r\n const remapValue = (\r\n value: number,\r\n start1: number,\r\n end1: number,\r\n start2: number,\r\n end2: number\r\n ): number => {\r\n const remapped =\r\n ((value - start1) * (end2 - start2)) / (end1 - start1) + start2\r\n return remapped > 0 ? remapped : 0\r\n }\r\n\r\n const animate = () => {\r\n clearContext()\r\n circles.current.forEach((circle: Circle, i: number) => {\r\n // Handle the alpha value\r\n const edge = [\r\n circle.x + circle.translateX - circle.size, // distance from left edge\r\n canvasSize.current.w -\r\n circle.x -\r\n circle.translateX -\r\n circle.size, // distance from right edge\r\n circle.y + circle.translateY - circle.size, // distance from top edge\r\n canvasSize.current.h -\r\n circle.y -\r\n circle.translateY -\r\n circle.size, // distance from bottom edge\r\n ]\r\n const closestEdge = edge.reduce((a, b) => Math.min(a, b))\r\n const remapClosestEdge = parseFloat(\r\n remapValue(closestEdge, 0, 20, 0, 1).toFixed(2)\r\n )\r\n if (remapClosestEdge > 1) {\r\n circle.alpha += 0.02\r\n if (circle.alpha > circle.targetAlpha) {\r\n circle.alpha = circle.targetAlpha\r\n }\r\n } else {\r\n circle.alpha = circle.targetAlpha * remapClosestEdge\r\n }\r\n circle.x += circle.dx + vx\r\n circle.y += circle.dy + vy\r\n circle.translateX +=\r\n (mouse.current.x / (staticity / circle.magnetism) -\r\n circle.translateX) /\r\n ease\r\n circle.translateY +=\r\n (mouse.current.y / (staticity / circle.magnetism) -\r\n circle.translateY) /\r\n ease\r\n\r\n drawCircle(circle, true)\r\n\r\n // circle gets out of the canvas\r\n if (\r\n circle.x < -circle.size ||\r\n circle.x > canvasSize.current.w + circle.size ||\r\n circle.y < -circle.size ||\r\n circle.y > canvasSize.current.h + circle.size\r\n ) {\r\n // remove the circle from the array\r\n circles.current.splice(i, 1)\r\n // create a new circle\r\n const newCircle = circleParams()\r\n drawCircle(newCircle)\r\n }\r\n })\r\n rafID.current = window.requestAnimationFrame(animate)\r\n }\r\n\r\n return (\r\n \r\n \r\n \r\n )\r\n}\r\n", "type": "registry:ui", "target": "components/ui/particles.tsx" } ], "cssVars": { "theme": { "animate-rainbow": "rainbow var(--speed, 2s) infinite linear" }, "light": { "--color-1": "oklch(66.2% 0.225 25.9)", "--color-2": "oklch(60.4% 0.26 302)", "--color-3": "oklch(69.6% 0.165 251)", "--color-4": "oklch(80.2% 0.134 225)", "--color-5": "oklch(90.7% 0.231 133)" }, "dark": { "--color-1": "oklch(66.2% 0.225 25.9)", "--color-2": "oklch(60.4% 0.26 302)", "--color-3": "oklch(69.6% 0.165 251)", "--color-4": "oklch(80.2% 0.134 225)", "--color-5": "oklch(90.7% 0.231 133)" } }, "css": { "@keyframes rainbow": { "0%": { "background-position": "0%" }, "100%": { "background-position": "200%" } } } }