{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "circuit-divider", "title": "Circuit Divider", "description": "DrawSVG circuit-board divider with animated paths and junction dots", "dependencies": [ "gsap" ], "files": [ { "path": "components/animation/circuit-divider.tsx", "content": "\"use client\";\n\nimport { useRef, useEffect } from \"react\";\nimport { gsap, ScrollTrigger } from \"@/lib/gsap-draw\";\n\n/**\n * CircuitDivider — PCB-style SVG trace that draws on scroll.\n *\n * Renders a horizontal circuit board trace with junction nodes,\n * perpendicular branches, and right-angle paths. DrawSVG reveals\n * the paths as the divider scrolls into view.\n *\n * Variants control visual weight and complexity:\n * - \"default\" — single horizontal trace with 2-3 branches\n * - \"complex\" — multiple parallel traces with more nodes\n * - \"minimal\" — thin single line with one node\n */\n\ninterface CircuitDividerProps {\n variant?: \"default\" | \"complex\" | \"minimal\";\n className?: string;\n}\n\nexport function CircuitDivider({\n variant = \"default\",\n className = \"\",\n}: CircuitDividerProps) {\n const svgRef = useRef(null);\n\n useEffect(() => {\n if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n\n const svg = svgRef.current;\n if (!svg) return;\n\n const paths = svg.querySelectorAll(\".circuit-path\");\n const dots = svg.querySelectorAll(\".circuit-dot\");\n\n const ctx = gsap.context(() => {\n // Set initial state\n gsap.set(paths, { drawSVG: \"0%\" });\n gsap.set(dots, { scale: 0, transformOrigin: \"center\" });\n\n const mainPaths = svg.querySelectorAll(\".circuit-path--main\");\n const branchPaths = svg.querySelectorAll(\".circuit-path--branch\");\n\n // Main traces + branches: scrub-linked to scroll (wipes left as you scroll down)\n const scrubTl = gsap.timeline({\n scrollTrigger: {\n trigger: svg,\n start: \"top 90%\",\n end: \"bottom 20%\",\n scrub: 1,\n },\n });\n\n scrubTl.to(mainPaths, {\n drawSVG: \"100%\",\n duration: 1,\n ease: \"none\",\n stagger: 0.1,\n });\n\n if (branchPaths.length) {\n scrubTl.to(\n branchPaths,\n {\n drawSVG: \"100%\",\n duration: 0.6,\n ease: \"none\",\n stagger: 0.1,\n },\n \"-=0.5\"\n );\n }\n\n scrubTl.to(\n dots,\n {\n scale: 1,\n duration: 0.2,\n ease: \"back.out(3)\",\n stagger: 0.05,\n },\n \"-=0.3\"\n );\n });\n\n return () => ctx.revert();\n }, []);\n\n return (\n
\n {variant === \"default\" && }\n {variant === \"complex\" && }\n {variant === \"minimal\" && }\n
\n );\n}\n\n/* ── SVG Variants ── */\n\nimport { forwardRef } from \"react\";\n\nconst STROKE = \"var(--color-foreground)\";\nconst ACCENT = \"var(--color-primary)\";\n\nconst DefaultCircuit = forwardRef(function DefaultCircuit(_, ref) {\n return (\n \n {/* Main trace */}\n \n {/* Parallel — offset +6px below main */}\n \n {/* Split branch — diagonal up */}\n \n {/* Split branch — diagonal down right */}\n \n {/* Split branch — short diagonal up right */}\n \n {/* Parallel to down-right branch */}\n \n {/* Junction nodes */}\n \n \n \n \n \n \n \n );\n});\n\nconst ComplexCircuit = forwardRef(function ComplexCircuit(_, ref) {\n return (\n \n {/* Upper main trace */}\n \n {/* Upper parallel — offset +5px */}\n \n {/* Lower main trace */}\n \n {/* Lower parallel — offset -5px */}\n \n {/* Diagonal connector — upper to lower */}\n \n {/* Diagonal connector — lower to upper */}\n \n {/* Split from upper — diagonal down-right */}\n \n {/* Split from lower — diagonal down */}\n \n {/* Split from upper right — diagonal up */}\n \n {/* Short diagonal spur */}\n \n {/* Junction nodes */}\n \n \n \n \n \n \n \n \n \n \n \n \n \n );\n});\n\nconst MinimalCircuit = forwardRef(function MinimalCircuit(_, ref) {\n return (\n \n {/* Single trace with diagonal kink */}\n \n {/* Parallel — offset +5px below */}\n \n {/* Split — short diagonal spur */}\n \n {/* Nodes */}\n \n \n \n \n );\n});\n", "type": "registry:ui" } ], "meta": { "layer": "signal", "pattern": "C" }, "type": "registry:ui" }