{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "button",
  "title": "Button",
  "description": "Primary/secondary/outline/ghost/link button with asChild, loading, icon slots and magnetic hover.",
  "dependencies": [
    "@radix-ui/react-slot",
    "class-variance-authority",
    "lucide-react"
  ],
  "registryDependencies": [
    "@hjconsult/lib",
    "@hjconsult/motion"
  ],
  "files": [
    {
      "path": "src/components/button/index.tsx",
      "content": "import { Slot, Slottable } from '@radix-ui/react-slot'\nimport { LoaderCircle } from 'lucide-react'\nimport type { ComponentPropsWithoutRef, ReactNode, Ref } from 'react'\nimport { cn, cva, focusRing, type VariantProps } from '../../lib'\nimport { Magnetic } from '../../motion/magnetic'\n\nexport const buttonVariants = cva(\n  [\n    'group/button relative isolate inline-flex shrink-0 cursor-pointer select-none items-center justify-center gap-2 whitespace-nowrap rounded-pill font-body font-medium',\n    'motion-safe:transition motion-safe:duration-[var(--hj-motion-duration-base)] motion-safe:ease-out-expo',\n    'disabled:pointer-events-none disabled:opacity-50 aria-busy:cursor-progress',\n    '[&_svg]:pointer-events-none [&_svg]:shrink-0',\n    focusRing,\n  ],\n  {\n    variants: {\n      variant: {\n        primary:\n          'bg-accent text-fg-on-accent shadow-1 hover:bg-accent-strong hover:shadow-2 hover:-translate-y-px active:translate-y-0 active:shadow-1',\n        secondary:\n          'border border-line bg-surface-raised text-fg hover:border-line-strong hover:shadow-2 hover:-translate-y-px active:translate-y-0 active:shadow-none',\n        outline:\n          'border border-line-strong bg-transparent text-fg hover:bg-surface hover:-translate-y-px active:translate-y-0',\n        ghost: 'bg-transparent text-fg-muted hover:bg-surface hover:text-fg',\n        link: [\n          'h-auto rounded-sm px-0 text-accent',\n          'bg-[linear-gradient(currentColor,currentColor)] bg-no-repeat bg-position-[0_100%] bg-size-[0%_1px] hover:bg-size-[100%_1px]',\n          'motion-safe:transition-[background-size,color]',\n        ],\n      },\n      size: {\n        sm: 'h-9 gap-1.5 px-4 text-small [&_svg]:size-4',\n        md: 'h-11 px-5 text-body [&_svg]:size-4',\n        lg: 'h-12 px-6 text-body [&_svg]:size-5',\n        xl: 'h-14 px-8 text-lead [&_svg]:size-5',\n        'icon-sm': 'size-9 [&_svg]:size-4',\n        'icon-md': 'size-11 [&_svg]:size-5',\n        'icon-lg': 'size-12 [&_svg]:size-5',\n      },\n    },\n    compoundVariants: [\n      { variant: 'link', size: ['sm', 'md', 'lg', 'xl'], class: 'h-auto px-0 py-0' },\n    ],\n    defaultVariants: { variant: 'primary', size: 'md' },\n  },\n)\n\nexport type ButtonVariant = NonNullable<VariantProps<typeof buttonVariants>['variant']>\nexport type ButtonSize = 'sm' | 'md' | 'lg' | 'xl'\n\nexport interface ButtonProps\n  extends ComponentPropsWithoutRef<'button'>,\n    Omit<VariantProps<typeof buttonVariants>, 'size'> {\n  size?: ButtonSize\n  /** Render the child element (e.g. a Next.js `Link`) with button styling. */\n  asChild?: boolean\n  /** Shows a spinner, sets `aria-busy` and disables interaction. */\n  loading?: boolean\n  iconStart?: ReactNode\n  iconEnd?: ReactNode\n  /** Wrap in `Magnetic` so the button leans toward the pointer. */\n  magnetic?: boolean\n  ref?: Ref<HTMLButtonElement>\n}\n\nexport function Button({\n  variant,\n  size = 'md',\n  asChild = false,\n  loading = false,\n  iconStart,\n  iconEnd,\n  magnetic = false,\n  disabled,\n  className,\n  children,\n  ref,\n  type,\n  ...props\n}: ButtonProps) {\n  const Comp = (asChild ? Slot : 'button') as 'button'\n  const element = (\n    <Comp\n      ref={ref}\n      className={cn(buttonVariants({ variant, size }), className)}\n      aria-busy={loading || undefined}\n      data-loading={loading || undefined}\n      disabled={disabled || loading || undefined}\n      type={asChild ? undefined : (type ?? 'button')}\n      {...props}\n    >\n      {loading ? (\n        <LoaderCircle aria-hidden=\"true\" className=\"animate-spin\" />\n      ) : iconStart ? (\n        <span aria-hidden=\"true\" className=\"inline-flex shrink-0\">\n          {iconStart}\n        </span>\n      ) : null}\n      <Slottable>{children}</Slottable>\n      {iconEnd ? (\n        <span\n          aria-hidden=\"true\"\n          className=\"inline-flex shrink-0 motion-safe:transition-transform motion-safe:duration-[var(--hj-motion-duration-base)] motion-safe:ease-out-expo group-hover/button:translate-x-0.5\"\n        >\n          {iconEnd}\n        </span>\n      ) : null}\n    </Comp>\n  )\n  return magnetic ? <Magnetic>{element}</Magnetic> : element\n}\n",
      "type": "registry:ui",
      "target": "hj/components/button/index.tsx"
    }
  ],
  "cssVars": {},
  "type": "registry:ui"
}