{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"shared-layout-bg","type":"registry:component","title":"Shared Layout Background","description":"A pill that glides between hovered items via motion's shared layout, with blur enter/exit.","author":"Saurabh <saurabh10102@gmail.com>","dependencies":["clsx","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/shared-layout-bg.tsx","type":"registry:component","target":"@components/motion/shared-layout-bg.tsx","content":"\"use client\";\n\nimport {\n  AnimatePresence,\n  motion,\n  useReducedMotion,\n  type Variants,\n} from \"motion/react\";\nimport {\n  Children,\n  cloneElement,\n  isValidElement,\n  useId,\n  useState,\n  type ReactElement,\n  type ReactNode,\n} from \"react\";\nimport { SPRING_LAYOUT } from \"@/lib/ease\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface SharedLayoutBgProps {\n  children: ReactNode;\n  className?: string;\n  /** Tailwind class applied to the moving pill. Defaults to a subtle foreground tint. */\n  pillClassName?: string;\n  /** Horizontal inset of the pill relative to each row (px). Default 20. */\n  inset?: number;\n}\n\nconst variants: Variants = {\n  initial: { opacity: 0, filter: \"blur(6px)\" },\n  animate: { opacity: 1, filter: \"blur(0px)\" },\n  exit: (isActive: boolean) =>\n    !isActive ? { opacity: 0, filter: \"blur(6px)\" } : {},\n};\n\nconst reducedVariants: Variants = {\n  initial: { opacity: 0 },\n  animate: { opacity: 1 },\n  exit: (isActive: boolean) => (!isActive ? { opacity: 0 } : {}),\n};\n\nexport function SharedLayoutBg({\n  children,\n  className,\n  pillClassName,\n  inset = 20,\n}: SharedLayoutBgProps) {\n  const [activeId, setActiveId] = useState<string | null>(null);\n  const uid = useId();\n  const reduce = useReducedMotion();\n\n  return (\n    // layoutRoot scopes the pill's layout projection to this list, so fixed or\n    // scrolled ancestors can't smear scroll offsets into its movement.\n    <motion.div\n      layoutRoot\n      onMouseLeave={() => setActiveId(null)}\n      className={cn(\"flex w-full flex-col\", className)}\n    >\n      {Children.toArray(children)\n        .filter(isValidElement)\n        .map((child, index) => {\n          const el = child as ReactElement<{ className?: string; onMouseEnter?: () => void; children?: ReactNode }>;\n          const childKey = el.key ? String(el.key) : `item-${index}`;\n          return cloneElement(\n            el,\n            {\n              key: childKey,\n              className: cn(\"relative\", el.props.className),\n              onMouseEnter: () => setActiveId(childKey),\n            },\n            <>\n              <AnimatePresence custom={activeId !== null}>\n                {activeId !== null ? (\n                  <motion.div\n                    variants={reduce ? reducedVariants : variants}\n                    initial=\"initial\"\n                    animate=\"animate\"\n                    exit=\"exit\"\n                    custom={activeId !== null}\n                    className=\"pointer-events-none absolute inset-y-0\"\n                    style={{ left: -inset, right: -inset }}\n                  >\n                    {activeId === childKey ? (\n                      <motion.div\n                        layoutId={`shared-bg-${uid}`}\n                        transition={reduce ? { duration: 0 } : SPRING_LAYOUT}\n                        className={cn(\n                          \"pointer-events-none h-full w-full rounded-2xl bg-primary/[0.06]\",\n                          pillClassName,\n                        )}\n                      />\n                    ) : null}\n                  </motion.div>\n                ) : null}\n              </AnimatePresence>\n              <div className=\"relative z-10\">{el.props.children}</div>\n            </>\n          );\n        })}\n    </motion.div>\n  );\n}\n"},{"path":"lib/ease.ts","type":"registry:lib","target":"@lib/ease.ts","content":"// Shared motion tokens. Easing curves mirror the CSS custom properties in\n// globals.css; springs are the canonical physics used across components.\n// Strong custom variants — defaults like `ease-in`/`ease-out` feel weak.\n\nexport const EASE_OUT = [0.16, 1, 0.3, 1] as const;\nexport const EASE_IN_OUT = [0.77, 0, 0.175, 1] as const;\nexport const EASE_DRAWER = [0.32, 0.72, 0, 1] as const;\n\n/** CSS string form of EASE_OUT for inline style transitions. */\nexport const EASE_OUT_CSS = \"cubic-bezier(0.16, 1, 0.3, 1)\";\n\n/** Press feedback on buttons and other tappable surfaces. */\nexport const SPRING_PRESS = {\n  type: \"spring\",\n  stiffness: 500,\n  damping: 30,\n  mass: 0.6,\n} as const;\n\n/** Content swaps — label/icon slots trading places inside a control. */\nexport const SPRING_SWAP = {\n  type: \"spring\",\n  stiffness: 460,\n  damping: 30,\n  mass: 0.55,\n} as const;\n\n/** Overlay panel entrances — modals and sheets summoned by pointer. */\nexport const SPRING_PANEL = {\n  type: \"spring\",\n  stiffness: 420,\n  damping: 40,\n  mass: 0.5,\n} as const;\n\n/** Shared-layout glides — pills, indicators and panels morphing between positions. */\nexport const SPRING_LAYOUT = {\n  type: \"spring\",\n  stiffness: 360,\n  damping: 32,\n  mass: 0.6,\n} as const;\n\n/** Cursor-follow physics for decorative mouse tracking (magnetic, tilt, dock). */\nexport const SPRING_MOUSE = {\n  stiffness: 200,\n  damping: 15,\n  mass: 0.3,\n} as const;\n"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts","content":"import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n"}]}