{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "particles-background",
  "type": "registry:ui",
  "title": "Particles Background",
  "description": "Floating particle field that drifts with subtle parallax. Ambient backdrop for heroes and sections.",
  "dependencies": [
    "@saasflare/ui"
  ],
  "files": [
    {
      "path": "components/ui/particles-background.tsx",
      "type": "registry:ui",
      "content": "\"use client\"\n\n/**\n * @fileoverview Floating particle animation background.\n * @author Saasflare™\n * Renders floating circles that drift with subtle parallax motion.\n * Pure CSS animation — no canvas, no requestAnimationFrame, minimal CPU.\n * @module packages/ui/components/ui/particles-background\n * @package ui\n *\n * @component\n * @example\n * import { ParticlesBackground } from '@saasflare/ui';\n * <div className=\"relative min-h-screen\">\n *   <ParticlesBackground />\n *   <div className=\"relative z-10\">Your content</div>\n * </div>\n *\n * @example\n * // Custom density and color\n * <ParticlesBackground count={30} color=\"var(--chart-2)\" maxSize={6} />\n */\n\nimport { useMemo } from \"react\"\nimport { cn } from \"@saasflare/ui\"\nimport { useSaasflareProps, type SaasflareComponentProps } from \"@saasflare/ui\"\nimport { useReducedMotion } from \"@saasflare/ui\"\n\n/** Props for the ParticlesBackground component. */\nexport interface ParticlesBackgroundProps extends SaasflareComponentProps {\n  /** Number of particles. Default: `20` */\n  count?: number\n  /** CSS color of particles. Default: `\"var(--primary)\"` */\n  color?: string\n  /** Maximum particle diameter in pixels. Default: `4` */\n  maxSize?: number\n  /** Minimum particle diameter in pixels. Default: `1` */\n  minSize?: number\n  /** Maximum particle opacity (0–1). Default: `0.3` */\n  maxOpacity?: number\n  /** Animation speed multiplier. Default: `1` */\n  speed?: number\n  /** Additional class names for the container. */\n  className?: string\n}\n\n/** Deterministic pseudo-random from seed (good enough for visual positions). */\nfunction seededRandom(seed: number): number {\n  const x = Math.sin(seed * 9301 + 49297) * 233280\n  return x - Math.floor(x)\n}\n\n/**\n * Ambient floating particles background.\n *\n * - CSS-only animation (no JS animation loop, GPU-composited)\n * - Deterministic positions (no layout shift between renders)\n * - Renders nothing when reduced motion is preferred or `animated` is disabled\n * - Lightweight: no canvas, no WebGL\n *\n * @component\n * @package ui\n */\nexport function ParticlesBackground({\n  count = 20,\n  color = \"var(--primary)\",\n  maxSize = 4,\n  minSize = 1,\n  maxOpacity = 0.3,\n  speed = 1,\n  className,\n  surface,\n  radius,\n  animated,\n  iconWeight,\n}: ParticlesBackgroundProps) {\n  const sf = useSaasflareProps({ surface, radius, animated, iconWeight })\n  const reduced = useReducedMotion()\n\n  const particles = useMemo(\n    () =>\n      Array.from({ length: count }, (_, i) => {\n        const r = (seed: number) => seededRandom(i * 100 + seed)\n        const size = minSize + r(1) * (maxSize - minSize)\n        const duration = (15 + r(2) * 25) / speed\n        const delay = r(3) * -duration\n\n        return {\n          id: i,\n          size,\n          x: r(4) * 100,\n          y: r(5) * 100,\n          opacity: 0.05 + r(6) * maxOpacity,\n          duration,\n          delay,\n          driftX: (r(7) - 0.5) * 40,\n          driftY: -20 - r(8) * 40,\n        }\n      }),\n    [count, minSize, maxSize, maxOpacity, speed],\n  )\n\n  if (reduced || !sf.animated) return null\n\n  return (\n    <div\n      className={cn(\"pointer-events-none absolute inset-0 overflow-hidden\", className)}\n      aria-hidden=\"true\"\n      data-slot=\"particles-background\"\n      data-surface={sf.surface}\n      data-radius={sf.radius}\n      data-animated={String(sf.animated)}\n    >\n      <style>{`\n        @keyframes sf-particle-drift {\n          0%, 100% { transform: translate(0, 0); }\n          50% { transform: translate(var(--drift-x), var(--drift-y)); }\n        }\n      `}</style>\n\n      {particles.map((p) => (\n        <div\n          key={p.id}\n          style={{\n            position: \"absolute\",\n            left: `${p.x}%`,\n            top: `${p.y}%`,\n            width: p.size,\n            height: p.size,\n            backgroundColor: color,\n            borderRadius: \"50%\",\n            opacity: p.opacity,\n            [\"--drift-x\" as string]: `${p.driftX}px`,\n            [\"--drift-y\" as string]: `${p.driftY}px`,\n            animation: `sf-particle-drift ${p.duration}s ease-in-out ${p.delay}s infinite`,\n          }}\n        />\n      ))}\n    </div>\n  )\n}\n",
      "target": "components/ui/particles-background.tsx"
    }
  ],
  "$meta": {
    "source": "https://ui.saasflare.io/r/particles-background.json"
  }
}
