{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "animated-tabs",
  "type": "registry:ui",
  "title": "Animated Tabs",
  "description": "An accessible horizontal tab selector built on Base UI with controlled state, keyboard navigation, disabled items, and a pointer-only shared hover indicator.",
  "files": [
    {
      "path": "registry/sonaui/animated-tabs/animated-tabs.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport { Tabs } from \"@base-ui/react/tabs\";\nimport { LayoutGroup, motion, useReducedMotion } from \"motion/react\";\nimport { type ReactNode, useId, useState } from \"react\";\n\nimport { motionTransition } from \"@/lib/sona-motion\";\nimport { cn } from \"@/lib/sona-utils\";\n\nexport interface AnimatedTabItem {\n  /** Stable value used to identify the tab. */\n  value: string;\n  /** Content displayed inside the tab trigger. */\n  title: ReactNode;\n  /** Whether the tab is unavailable. @default false */\n  disabled?: boolean;\n  /** ID of the external tab panel controlled by this tab. */\n  ariaControls?: string;\n}\n\nexport interface AnimatedTabsProps {\n  /** Tabs displayed in the horizontal tab list. */\n  tabs: AnimatedTabItem[];\n  /** Controlled active tab value. */\n  value?: string;\n  /** Initially active tab for uncontrolled usage. */\n  defaultValue?: string;\n  /** Called when the active tab changes. */\n  onValueChange?: (value: string) => void;\n  /** Accessible label for the tab list. @default \"Tabs\" */\n  ariaLabel?: string;\n  /** Background class for the shared active indicator. @default \"bg-accent\" */\n  indicatorClassName?: string;\n  /** Background class for the active tab. @default \"bg-muted\" */\n  activeTabClassName?: string;\n  /** Additional classes for the root container. */\n  className?: string;\n  /** Additional classes for the tab list. */\n  listClassName?: string;\n}\n\nexport default function AnimatedTabs({\n  tabs,\n  value,\n  defaultValue,\n  onValueChange,\n  ariaLabel = \"Tabs\",\n  indicatorClassName = \"bg-accent\",\n  activeTabClassName = \"bg-muted\",\n  className,\n  listClassName,\n}: AnimatedTabsProps) {\n  const fallbackValue = tabs.find((tab) => !tab.disabled)?.value;\n  const [internalValue, setInternalValue] = useState(\n    defaultValue ?? fallbackValue,\n  );\n  const activeValue = value ?? internalValue;\n  const layoutId = useId();\n  const shouldReduceMotion = useReducedMotion();\n\n  return (\n    <Tabs.Root\n      value={value}\n      defaultValue={defaultValue ?? fallbackValue}\n      orientation=\"horizontal\"\n      onValueChange={(nextValue) => {\n        if (typeof nextValue !== \"string\") return;\n        if (value === undefined) setInternalValue(nextValue);\n        onValueChange?.(nextValue);\n      }}\n      className={cn(\"relative w-fit overflow-x-auto border-b p-2\", className)}\n    >\n      <LayoutGroup id={layoutId}>\n        <Tabs.List\n          aria-label={ariaLabel}\n          className={cn(\"flex gap-2\", listClassName)}\n        >\n          {tabs.map((tab) => (\n            <Tabs.Tab\n              key={tab.value}\n              value={tab.value}\n              disabled={tab.disabled}\n              aria-controls={tab.ariaControls}\n              className={(state) =>\n                cn(\n                  \"relative flex cursor-pointer items-center rounded-xl p-2\",\n                  \"transition-colors duration-200\",\n                  \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n                  \"disabled:pointer-events-none disabled:opacity-50\",\n                  state.active && activeTabClassName,\n                )\n              }\n            >\n              {activeValue === tab.value && (\n                <motion.span\n                  aria-hidden=\"true\"\n                  layoutId={`${layoutId}-active`}\n                  className={cn(\n                    \"pointer-events-none absolute inset-0 rounded-xl\",\n                    indicatorClassName,\n                  )}\n                  transition={\n                    shouldReduceMotion\n                      ? motionTransition.reduced\n                      : motionTransition.spatial\n                  }\n                />\n              )}\n              <span className=\"relative\">{tab.title}</span>\n            </Tabs.Tab>\n          ))}\n        </Tabs.List>\n      </LayoutGroup>\n    </Tabs.Root>\n  );\n}\n",
      "target": "components/ui/animated-tabs/animated-tabs.tsx"
    }
  ],
  "dependencies": [
    "@base-ui/react",
    "motion"
  ],
  "registryDependencies": [
    "@sona-ui/sona-utils",
    "@sona-ui/sona-motion"
  ]
}