{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "magnetic-tabs",
  "title": "Magnetic Tabs",
  "description": "Accessible tabs with a gently responsive label and a clear active surface.",
  "dependencies": [
    "@base-ui/react@^1.7.0",
    "motion@^13.1.0"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/default/magnetic-tabs/magnetic-tabs.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Tabs } from \"@base-ui/react/tabs\"\nimport {\n  motion,\n  useMotionValue,\n  useReducedMotion,\n  useSpring,\n} from \"motion/react\"\nimport { cn } from \"@/lib/utils\"\n\nexport interface MagneticTabItem {\n  value: string\n  label: React.ReactNode\n  content: React.ReactNode\n  disabled?: boolean\n}\n\nexport interface MagneticTabsProps extends Omit<\n  React.ComponentPropsWithoutRef<typeof Tabs.Root>,\n  \"children\" | \"defaultValue\" | \"onValueChange\" | \"value\"\n> {\n  items: MagneticTabItem[]\n  defaultValue?: string\n  value?: string\n  onValueChange?: (value: string) => void\n  className?: string\n}\n\ninterface MagneticTabProps {\n  item: MagneticTabItem\n}\n\nfunction MagneticTab({ item }: MagneticTabProps) {\n  const prefersReducedMotion = useReducedMotion()\n  const x = useMotionValue(0)\n  const y = useMotionValue(0)\n  const springX = useSpring(x, { stiffness: 500, damping: 34, mass: 0.35 })\n  const springY = useSpring(y, { stiffness: 500, damping: 34, mass: 0.35 })\n\n  function handlePointerMove(event: React.PointerEvent<HTMLButtonElement>) {\n    if (prefersReducedMotion || event.pointerType !== \"mouse\") return\n\n    const bounds = event.currentTarget.getBoundingClientRect()\n    x.set(((event.clientX - bounds.left) / bounds.width - 0.5) * 6)\n    y.set(((event.clientY - bounds.top) / bounds.height - 0.5) * 4)\n  }\n\n  function resetPosition() {\n    x.set(0)\n    y.set(0)\n  }\n\n  return (\n    <Tabs.Tab\n      data-slot=\"magnetic-tab\"\n      value={item.value}\n      disabled={item.disabled}\n      onPointerMove={handlePointerMove}\n      onPointerLeave={resetPosition}\n      className=\"text-muted-foreground hover:text-foreground focus-visible:ring-ring focus-visible:ring-offset-muted data-active:text-foreground relative z-10 min-h-11 flex-1 cursor-default rounded-[calc(var(--radius)-0.25rem)] px-4 py-2 text-sm font-medium whitespace-nowrap outline-none select-none focus-visible:ring-2 focus-visible:ring-offset-2 data-disabled:pointer-events-none data-disabled:opacity-45\"\n    >\n      <motion.span\n        className=\"inline-flex\"\n        style={prefersReducedMotion ? undefined : { x: springX, y: springY }}\n      >\n        {item.label}\n      </motion.span>\n    </Tabs.Tab>\n  )\n}\n\nexport const MagneticTabs = React.forwardRef<\n  React.ComponentRef<typeof Tabs.Root>,\n  MagneticTabsProps\n>(function MagneticTabs(\n  { items, defaultValue, value, onValueChange, className, ...rootProps },\n  forwardedRef\n) {\n  const firstEnabledItem = items.find((item) => !item.disabled)\n\n  if (!firstEnabledItem) return null\n\n  return (\n    <Tabs.Root\n      {...rootProps}\n      data-slot=\"magnetic-tabs\"\n      ref={forwardedRef}\n      value={value}\n      defaultValue={defaultValue ?? firstEnabledItem.value}\n      onValueChange={(nextValue) => {\n        if (typeof nextValue === \"string\") onValueChange?.(nextValue)\n      }}\n      className={cn(\"w-full\", className)}\n    >\n      <Tabs.List\n        activateOnFocus\n        data-slot=\"magnetic-tabs-list\"\n        className=\"bg-muted relative isolate flex w-full rounded-[var(--radius)] p-1\"\n      >\n        {items.map((item) => (\n          <MagneticTab key={item.value} item={item} />\n        ))}\n        <Tabs.Indicator\n          data-slot=\"magnetic-tabs-indicator\"\n          renderBeforeHydration\n          className=\"border-border/70 bg-background absolute top-1 bottom-1 left-0 -z-0 w-[var(--active-tab-width)] translate-x-[var(--active-tab-left)] rounded-[calc(var(--radius)-0.25rem)] border shadow-sm transition-[width,translate] duration-200 ease-out motion-reduce:transition-none\"\n        />\n      </Tabs.List>\n\n      <div\n        data-slot=\"magnetic-tabs-panels\"\n        className=\"bg-card text-card-foreground mt-3 overflow-hidden rounded-[var(--radius)] border\"\n      >\n        {items.map((item) => (\n          <Tabs.Panel\n            data-slot=\"magnetic-tabs-panel\"\n            key={item.value}\n            value={item.value}\n            className=\"focus-visible:ring-ring p-5 text-sm leading-6 transition-[opacity,translate] duration-200 ease-out outline-none focus-visible:ring-2 focus-visible:ring-inset data-[ending-style]:-translate-y-1 data-[ending-style]:opacity-0 data-[starting-style]:translate-y-1 data-[starting-style]:opacity-0 motion-reduce:transition-none\"\n          >\n            {item.content}\n          </Tabs.Panel>\n        ))}\n      </div>\n    </Tabs.Root>\n  )\n})\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "navigation",
    "tabs",
    "motion"
  ],
  "type": "registry:ui"
}