{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "components-tabs",
  "title": "Tabs",
  "description": "Animated tablist with accessible keyboard navigation and animated underline.",
  "dependencies": [
    "motion",
    "class-variance-authority"
  ],
  "registryDependencies": [
    "@azemmur/primitives-button"
  ],
  "files": [
    {
      "path": "registry/components/azemmur/tabs/index.tsx",
      "content": "// Copyright (c) 2026 raouf.codes - Azemmur\n\nimport { TabsContent } from '@/components/azemmur/components/azemmur/tabs/tabs-content';\nimport { TabsList } from '@/components/azemmur/components/azemmur/tabs/tabs-list';\nimport { TabPanel } from '@/components/azemmur/components/azemmur/tabs/tabs-panel';\nimport { TabsRoot } from '@/components/azemmur/components/azemmur/tabs/tabs-root';\nimport { TabTrigger } from '@/components/azemmur/components/azemmur/tabs/tabs-trigger';\n\ntype TabsComponent = typeof TabsRoot & {\n  List: typeof TabsList;\n  Trigger: typeof TabTrigger;\n  Content: typeof TabsContent;\n  Panel: typeof TabPanel;\n};\n\nconst Tabs = TabsRoot as TabsComponent;\n\nTabs.Trigger = TabTrigger;\nTabs.List = TabsList;\nTabs.Content = TabsContent;\nTabs.Panel = TabPanel;\n\nexport { Tabs };\n",
      "type": "registry:ui",
      "target": "components/azemmur/tabs/index.tsx"
    },
    {
      "path": "registry/components/azemmur/tabs/tabs-root.tsx",
      "content": "// Copyright (c) 2026 raouf.codes - Azemmur\n\n'use client';\n\nimport { cn } from '@/lib/utils';\nimport React, {\n  useId,\n  useState,\n  useLayoutEffect,\n  useRef,\n  useMemo,\n  useCallback,\n  ComponentProps,\n} from 'react';\nimport { TabsContext } from '@/components/azemmur/components/azemmur/tabs/tabs-context';\nimport type { TabsDirection } from '@/components/azemmur/components/azemmur/tabs/tabs-context';\nimport { TabsVariantProps } from '@/components/azemmur/components/azemmur/tabs/tabs-variants';\n\ninterface TabsRootProps extends ComponentProps<'div'>, TabsVariantProps {\n  value?: string;\n  defaultValue?: string;\n  onValueChange?: (value: string) => void;\n  direction?: TabsDirection;\n  activation?: 'auto' | 'manual';\n  className?: string;\n  children: React.ReactNode;\n}\n\nfunction TabsRoot({\n  value,\n  defaultValue,\n  onValueChange,\n  direction,\n  activation = 'manual',\n  className,\n  intent,\n  styling,\n  visuals,\n  size,\n  shape,\n  children,\n  ...props\n}: TabsRootProps) {\n  const tabsId = useId();\n  const [internalValue, setInternalValue] = useState(defaultValue ?? '');\n  const resolvedDirection = direction ?? 'ltr';\n  const isLtr = resolvedDirection === 'ltr';\n\n  const activeValue = value ?? internalValue;\n\n  const isControlled = value !== undefined;\n\n  const triggerRefs = useRef<Map<string, HTMLButtonElement>>(new Map());\n\n  const registerTrigger = useCallback(\n    (val: string, node: HTMLButtonElement | null) => {\n      if (node) {\n        const prevSize = triggerRefs.current.size;\n        triggerRefs.current.set(val, node);\n        if (!isControlled && !activeValue && prevSize === 0) {\n          setInternalValue(val);\n        }\n      } else {\n        triggerRefs.current.delete(val);\n      }\n    },\n    [isControlled, activeValue],\n  );\n\n  const handleValueChange = useCallback(\n    (next: string) => {\n      if (!isControlled) setInternalValue(next);\n      onValueChange?.(next);\n    },\n    [onValueChange, isControlled],\n  );\n\n  const handleKeyDown = useCallback(\n    (e: React.KeyboardEvent, val: string) => {\n      const values = Array.from(triggerRefs.current.keys());\n      const index = values.indexOf(val);\n      if (index === -1) return;\n\n      const forward = isLtr ? 'ArrowRight' : 'ArrowLeft';\n      const backward = isLtr ? 'ArrowLeft' : 'ArrowRight';\n\n      let next = index;\n\n      if (e.key === forward) next = (index + 1) % values.length;\n      else if (e.key === backward)\n        next = (index - 1 + values.length) % values.length;\n      else if (e.key === 'Home') next = 0;\n      else if (e.key === 'End') next = values.length - 1;\n      else return;\n\n      e.preventDefault();\n      const nextValue = values[next] ?? '';\n      triggerRefs.current.get(nextValue)?.focus();\n      if (activation === 'auto') handleValueChange(nextValue);\n    },\n    [isLtr, activation, handleValueChange],\n  );\n\n  useLayoutEffect(() => {\n    if (isControlled) return;\n    if (!activeValue && triggerRefs.current.size > 0) {\n      const first = triggerRefs.current.keys().next().value;\n      if (first) setInternalValue(first);\n    }\n  }, [activeValue, isControlled]);\n\n  const memoizedValue = useMemo(\n    () => ({\n      tabsId,\n      activeValue,\n      onValueChange: handleValueChange,\n      registerTrigger,\n      handleKeyDown,\n      direction: resolvedDirection,\n      activation,\n      getTriggerId: (val: string) => `${tabsId}-trigger-${val}`,\n      getPanelId: (val: string) => `${tabsId}-panel-${val}`,\n      intent,\n      styling,\n      visuals,\n      size,\n      shape,\n    }),\n    [\n      tabsId,\n      activeValue,\n      handleValueChange,\n      registerTrigger,\n      handleKeyDown,\n      resolvedDirection,\n      activation,\n      intent,\n      styling,\n      visuals,\n      size,\n      shape,\n    ],\n  );\n\n  return (\n    <TabsContext.Provider value={memoizedValue}>\n      <div className={cn(className)} {...props}>\n        {children}\n      </div>\n    </TabsContext.Provider>\n  );\n}\n\nexport { TabsRoot, type TabsRootProps };\n",
      "type": "registry:ui",
      "target": "components/azemmur/tabs/tabs-root.tsx"
    },
    {
      "path": "registry/components/azemmur/tabs/tabs-context.tsx",
      "content": "// Copyright (c) 2026 raouf.codes - Azemmur\n'use client';\n\nimport { createContext, useContext } from 'react';\nimport { TabsVariantProps } from '@/components/azemmur/components/azemmur/tabs/tabs-variants';\n\ntype TabsDirection = 'ltr' | 'rtl';\n\ninterface TabsContextValue extends TabsVariantProps {\n  tabsId: string;\n  direction: TabsDirection;\n  activeValue?: string;\n  onValueChange: (value: string) => void;\n  registerTrigger: (value: string, node: HTMLButtonElement | null) => void;\n  handleKeyDown: (e: React.KeyboardEvent, value: string) => void;\n  getPanelId: (value: string) => string;\n  getTriggerId: (value: string) => string;\n}\n\nconst TabsContext = createContext<TabsContextValue | null>(null);\n\nconst useTabs = () => {\n  const context = useContext(TabsContext);\n  if (!context) {\n    throw new Error('Tab components must be used within <Tabs />');\n  }\n  return context;\n};\n\nexport { TabsContext, useTabs };\nexport type { TabsDirection };\n",
      "type": "registry:hook",
      "target": "components/azemmur/tabs/tabs-context.tsx"
    },
    {
      "path": "registry/components/azemmur/tabs/tabs-list.tsx",
      "content": "// Copyright (c) 2026 raouf.codes - Azemmur\n'use client';\n\nimport { cn } from '@/lib/utils';\nimport React, { ComponentProps } from 'react';\nimport { useTabs } from '@/components/azemmur/components/azemmur/tabs/tabs-context';\nimport { listVariants } from '@/components/azemmur/components/azemmur/tabs/tabs-variants';\n\ninterface TabsListProps extends ComponentProps<'div'> {\n  className?: string;\n}\n\nfunction TabsList({ className, children, ...props }: TabsListProps) {\n  const { direction, intent, visuals, size, shape } = useTabs();\n  return (\n    <div\n      role=\"tablist\"\n      aria-orientation=\"horizontal\"\n      dir={direction}\n      className={cn(listVariants({ intent, visuals, size, shape }), className)}\n      {...props}\n    >\n      {children}\n    </div>\n  );\n}\n\nexport { TabsList, type TabsListProps };\n",
      "type": "registry:ui",
      "target": "components/azemmur/tabs/tabs-list.tsx"
    },
    {
      "path": "registry/components/azemmur/tabs/tabs-trigger.tsx",
      "content": "// Copyright (c) 2026 raouf.codes - Azemmur\n\n'use client';\n\nimport { cn } from '@/lib/utils';\nimport {\n  useEffect,\n  useRef,\n  useImperativeHandle,\n  type ComponentPropsWithoutRef,\n  type Ref,\n} from 'react';\nimport { useTabs } from '@/components/azemmur/components/azemmur/tabs/tabs-context';\nimport { TabsIndicator as Indicator } from '@/components/azemmur/components/azemmur/tabs/tabs-indicator';\nimport { triggerVariants } from '@/components/azemmur/components/azemmur/tabs/tabs-variants';\nimport { Button as ButtonPrimitive } from '@/components/azemmur/components/primitives/button';\n\ninterface TabTriggerProps extends Omit<\n  ComponentPropsWithoutRef<typeof ButtonPrimitive>,\n  'asChild'\n> {\n  value: string;\n  triggerClassName?: string;\n  indicatorClassName?: string;\n  ref?: Ref<HTMLButtonElement>;\n}\n\nfunction TabTrigger({\n  value,\n  children,\n  className,\n  triggerClassName,\n  indicatorClassName,\n  ref,\n  ...props\n}: TabTriggerProps) {\n  const {\n    tabsId,\n    activeValue,\n    onValueChange,\n    registerTrigger,\n    handleKeyDown,\n    getTriggerId,\n    getPanelId,\n    intent,\n    styling,\n    visuals,\n    shape,\n  } = useTabs();\n  const isActive = value === activeValue;\n  const internalRef = useRef<HTMLButtonElement>(null);\n\n  useImperativeHandle(ref, () => internalRef.current!);\n\n  useEffect(() => {\n    registerTrigger(value, internalRef.current);\n    return () => registerTrigger(value, null);\n  }, [value, registerTrigger]);\n\n  return (\n    <div className={cn('relative inline-block', className)}>\n      {isActive && (\n        <Indicator\n          layoutId={`active-tab-${tabsId}`}\n          className={indicatorClassName}\n          data-state={isActive ? 'active' : 'inactive'}\n          intent={intent}\n          styling={styling}\n          visuals={visuals}\n          shape={shape}\n        />\n      )}\n\n      <ButtonPrimitive\n        role=\"tab\"\n        aria-selected={isActive}\n        aria-controls={getPanelId(value)}\n        id={getTriggerId(value)}\n        ref={internalRef}\n        data-state={isActive ? 'active' : 'inactive'}\n        tabIndex={isActive ? 0 : -1}\n        onClick={(e) => {\n          onValueChange(value);\n          props.onClick?.(e);\n        }}\n        onKeyDown={(e) => {\n          handleKeyDown(e, value);\n          props.onKeyDown?.(e);\n        }}\n        className={cn(\n          triggerVariants({\n            intent,\n            visuals,\n            styling,\n            shape,\n          }),\n          triggerClassName,\n        )}\n        {...props}\n      >\n        {children}\n      </ButtonPrimitive>\n    </div>\n  );\n}\n\nexport { TabTrigger, type TabTriggerProps };\n",
      "type": "registry:ui",
      "target": "components/azemmur/tabs/tabs-trigger.tsx"
    },
    {
      "path": "registry/components/azemmur/tabs/tabs-indicator.tsx",
      "content": "// Copyright (c) 2026 raouf.codes - Azemmur\n\n'use client';\n\nimport { cn } from '@/lib/utils';\nimport { type VariantProps } from 'class-variance-authority';\nimport { motion } from 'motion/react';\nimport type { ComponentProps } from 'react';\nimport { indicatorVariants } from '@/components/azemmur/components/azemmur/tabs/tabs-variants';\n\ntype TabsIndicatorProps = VariantProps<typeof indicatorVariants> &\n  ComponentProps<typeof motion.div>;\n\nfunction TabsIndicator({\n  intent,\n  styling,\n  shape,\n  visuals,\n  className,\n  ...props\n}: TabsIndicatorProps) {\n  return (\n    <motion.div\n      aria-hidden=\"true\"\n      layout\n      {...props}\n      className={cn(\n        indicatorVariants({\n          intent,\n          styling,\n          shape,\n          visuals,\n        }),\n        className,\n      )}\n      transition={{ type: 'spring', stiffness: 400, damping: 30, mass: 1 }}\n    />\n  );\n}\n\nexport { TabsIndicator, type TabsIndicatorProps };\n",
      "type": "registry:ui",
      "target": "components/azemmur/tabs/tabs-indicator.tsx"
    },
    {
      "path": "registry/components/azemmur/tabs/tabs-content.tsx",
      "content": "// Copyright (c) 2026 raouf.codes - Azemmur\n'use client';\n\nimport { cn } from '@/lib/utils';\nimport React, { ComponentProps } from 'react';\nimport { useTabs } from '@/components/azemmur/components/azemmur/tabs/tabs-context';\n\ninterface TabsContentProps extends ComponentProps<'div'> {\n  className?: string;\n}\n\nfunction TabsContent({ className, children, ...props }: TabsContentProps) {\n  const { direction } = useTabs();\n  return (\n    <div\n      dir={direction}\n      className={cn('overflow-hidden', className)}\n      {...props}\n    >\n      {children}\n    </div>\n  );\n}\n\nexport { TabsContent, type TabsContentProps };\n",
      "type": "registry:ui",
      "target": "components/azemmur/tabs/tabs-content.tsx"
    },
    {
      "path": "registry/components/azemmur/tabs/tabs-panel.tsx",
      "content": "// Copyright (c) 2026 raouf.codes - Azemmur\n\n'use client';\n\nimport { cn } from '@/lib/utils';\nimport { motion } from 'motion/react';\nimport React, { type ComponentProps } from 'react';\nimport { useTabs } from '@/components/azemmur/components/azemmur/tabs/tabs-context';\n\nexport interface TabPanelProps extends ComponentProps<typeof motion.div> {\n  value: string;\n}\n\nexport function TabPanel({\n  value,\n  children,\n  className,\n  ...props\n}: TabPanelProps) {\n  const { activeValue, getPanelId, getTriggerId } = useTabs();\n  const isActive = activeValue === value;\n\n  if (!isActive) return null;\n\n  return (\n    <motion.div\n      key={value}\n      role=\"tabpanel\"\n      id={getPanelId(value)}\n      aria-labelledby={getTriggerId(value)}\n      tabIndex={0}\n      initial={{ opacity: 0, y: '100%' }}\n      animate={{ opacity: 1, y: '0%' }}\n      transition={{ duration: 0.2, ease: 'easeOut' }}\n      className={cn(\n        'mt-4 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n        className,\n      )}\n      {...props}\n    >\n      {children}\n    </motion.div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/azemmur/tabs/tabs-panel.tsx"
    },
    {
      "path": "registry/components/azemmur/tabs/tabs-variants.ts",
      "content": "// Copyright (c) 2026 raouf.codes - Azemmur\n\nimport { cva, type VariantProps } from 'class-variance-authority';\n\nconst listVariants = cva(\n  'relative inline-flex whitespace-nowrap overflow-visible gap-2',\n  {\n    variants: {\n      intent: {\n        primary: 'text-primary border-primary',\n        secondary: 'text-secondary border-secondary',\n        accent: 'text-accent border-accent',\n      },\n      visuals: {\n        filled: '',\n        subtle: 'bg-transparent px-8',\n        classic: 'bg-transparent border-b px-8',\n        outline: 'bg-transparent border-2 overflow-hidden',\n        levitate: 'p-2 border-2',\n      },\n      size: {\n        sm: 'text-sm',\n        md: 'text-base',\n        lg: 'text-lg',\n      },\n      shape: {\n        rounded: 'rounded-md',\n        pill: 'rounded-full',\n        sharp: 'rounded-none',\n      },\n    },\n    compoundVariants: [\n      {\n        visuals: 'classic',\n        shape: ['rounded', 'pill'],\n        className: 'rounded-none',\n      },\n      {\n        intent: ['primary', 'secondary', 'accent'],\n        visuals: ['filled', 'levitate'],\n        className: 'bg-current/10',\n      },\n    ],\n    defaultVariants: {\n      intent: 'primary',\n      visuals: 'classic',\n      size: 'md',\n      shape: 'rounded',\n    },\n  },\n);\n\nconst triggerVariants = cva(\n  [\n    'block px-6 py-2 font-medium transition-colors z-10 relative bg-transparent',\n    'focus-visible:outline-none focus-visible:ring-2',\n    'after:content-[attr(data-label)] after:block after:h-0 after:invisible',\n    '[&[data-state=\"active\"]]:font-bold [&[data-state=\"active\"]]:text-foreground',\n  ].join(' '),\n  {\n    variants: {\n      intent: {\n        primary: 'text-primary',\n        secondary: 'text-secondary',\n        accent: 'text-accent',\n      },\n      visuals: {\n        classic: null,\n        outline: 'focus-visible:rounded-none',\n      },\n      styling: {\n        underline: null,\n        minimal: null,\n        solid: null,\n      },\n      shape: {\n        rounded: 'rounded-md',\n        pill: 'rounded-full',\n        sharp: 'rounded-none',\n      },\n    },\n    compoundVariants: [\n      {\n        intent: 'primary',\n        styling: 'solid',\n        className: '[&[data-state=\"active\"]]:text-primary-foreground',\n      },\n      {\n        intent: 'secondary',\n        styling: 'solid',\n        className: '[&[data-state=\"active\"]]:text-secondary-foreground',\n      },\n      {\n        intent: 'accent',\n        styling: 'solid',\n        className: '[&[data-state=\"active\"]]:text-accent-foreground',\n      },\n      {\n        visuals: 'classic',\n        shape: ['pill', 'rounded'],\n        className: 'rounded-b-none rounded-t-md',\n      },\n    ],\n    defaultVariants: {\n      intent: 'primary',\n      styling: 'underline',\n      shape: 'rounded',\n    },\n  },\n);\n\nconst indicatorVariants = cva(\n  'absolute pointer-events-none transition-colors',\n  {\n    variants: {\n      intent: {\n        primary: 'bg-primary',\n        secondary: 'bg-secondary',\n        accent: 'bg-accent',\n      },\n      styling: {\n        underline: 'bottom-0 h-1 start-0 end-0 bg-primary',\n        minimal: 'bottom-0 h-2 w-2 left-1/2 -translate-x-1/2 bg-current',\n        solid: 'inset-0',\n      },\n      visuals: {\n        classic: null,\n        outline: null,\n      },\n      shape: {\n        rounded: 'rounded-md',\n        pill: 'rounded-full',\n        sharp: 'rounded-none',\n      },\n    },\n    compoundVariants: [\n      {\n        styling: 'solid',\n        visuals: 'classic',\n        shape: ['pill', 'rounded'],\n        className: 'rounded-b-none rounded-t-md',\n      },\n      {\n        styling: 'solid',\n        visuals: 'outline',\n        className: 'rounded-none',\n      },\n      {\n        styling: ['underline', 'minimal'],\n        className: '[&[data-state=\"active\"]]:bg-foreground',\n      },\n    ],\n    defaultVariants: {\n      styling: 'underline',\n      visuals: 'classic',\n      shape: 'rounded',\n    },\n  },\n);\n\ntype TabsVariantProps = VariantProps<typeof listVariants> &\n  VariantProps<typeof triggerVariants>;\n\nexport {\n  indicatorVariants,\n  listVariants,\n  triggerVariants,\n  type TabsVariantProps,\n};\n",
      "type": "registry:ui",
      "target": "components/azemmur/tabs/tabs-variants.ts"
    }
  ],
  "type": "registry:ui"
}