{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "button",
  "title": "Button",
  "dependencies": [
    "@base-ui/react",
    "class-variance-authority"
  ],
  "registryDependencies": [
    "https://ui.hotfix.jobs/r/recipes.json",
    "https://ui.hotfix.jobs/r/spinner.json",
    "https://ui.hotfix.jobs/r/tokens.json",
    "https://ui.hotfix.jobs/r/utils.json"
  ],
  "files": [
    {
      "path": "registry/components/ui/button.tsx",
      "content": "\"use client\";\n\nimport { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Spinner } from \"@/components/ui/spinner\";\nimport type * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { disabled, colorTransition, popupTriggerOpen, selectionFocus } from \"@/lib/recipes\";\nexport const buttonVariants = cva(\n  [\n    \"relative inline-flex shrink-0 items-center justify-center whitespace-nowrap\",\n    \"[&_svg]:pointer-events-none [&_svg]:shrink-0\",\n    // Extend tap target to 44px on coarse pointers without changing visual size.\n    \"pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11\",\n    // Keyboard focus mirrors hover per variant (no outside ring).\n    \"outline-none\",\n    colorTransition,\n    disabled,\n  ].join(\" \"),\n  {\n    defaultVariants: { size: \"md\", variant: \"primary\", shape: \"rounded\" },\n    variants: {\n      size: {\n        sm: \"h-6 px-2.5 gap-1.5 text-mini font-medium\",\n        md: \"h-8 px-3.5 gap-2 text-small font-medium\",\n        lg: \"h-10 px-4 gap-2 text-regular font-medium\",\n      },\n      shape: {\n        rounded: \"rounded-[var(--radius-8)]\",\n        pill: \"rounded-full\",\n      },\n      variant: {\n        primary:\n          \"bg-primary text-on-primary hover:bg-primary-hover focus-visible:bg-primary-hover focus-visible:shadow-[inset_0_-2px_0_var(--on-primary)] data-[popup-open]:bg-primary-hover active:bg-primary-active\",\n        secondary:\n          cn(\"bg-fill-1 text-ink hover:bg-fill-2 focus-visible:bg-fill-2 active:bg-layer-hover\", popupTriggerOpen, selectionFocus),\n        outlined:\n          cn(\"border border-hairline bg-transparent text-ink hover:bg-layer-hover focus-visible:bg-layer-hover active:bg-layer-hover\", popupTriggerOpen, selectionFocus),\n        soft:\n          cn(\"bg-primary-soft text-on-primary-soft hover:bg-primary-soft-hover focus-visible:bg-primary-soft-hover data-[popup-open]:bg-primary-soft-hover active:bg-primary-soft-active\", selectionFocus),\n        tertiary:\n          cn(\"bg-transparent text-ink hover:bg-layer-hover focus-visible:bg-layer-hover active:bg-layer-hover\", popupTriggerOpen, selectionFocus),\n        warning:\n          \"bg-warning text-warning-fg hover:bg-warning-hover focus-visible:bg-warning-hover focus-visible:shadow-[inset_0_-2px_0_var(--warning-fg)] data-[popup-open]:bg-warning-hover active:bg-warning-active\",\n        destructive:\n          \"bg-error text-error-fg hover:bg-error-hover focus-visible:bg-error-hover focus-visible:shadow-[inset_0_-2px_0_var(--error-fg)] data-[popup-open]:bg-error-hover active:bg-error-active\",\n      },\n    },\n  },\n);\n\nexport interface ButtonProps extends useRender.ComponentProps<\"button\"> {\n  variant?: VariantProps<typeof buttonVariants>[\"variant\"];\n  size?: VariantProps<typeof buttonVariants>[\"size\"];\n  shape?: VariantProps<typeof buttonVariants>[\"shape\"];\n  icon?: React.ReactNode;\n  iconPosition?: \"left\" | \"right\";\n  loading?: boolean;\n  disabled?: boolean;\n}\n\nconst iconOnlyWidth: Record<NonNullable<ButtonProps[\"size\"]>, string> = {\n  sm: \"w-6 px-0\",\n  md: \"w-8 px-0\",\n  lg: \"w-10 px-0\",\n};\n\nexport function Button({\n  className,\n  variant,\n  size,\n  shape,\n  icon,\n  iconPosition = \"left\",\n  loading,\n  disabled: isDisabled,\n  render,\n  children,\n  ...props\n}: ButtonProps): React.ReactElement {\n  const effectiveSize: NonNullable<ButtonProps[\"size\"]> = size ?? \"md\";\n  const isIconOnly = icon != null && !children;\n  const iconOnly = isIconOnly ? iconOnlyWidth[effectiveSize] : \"\";\n\n  if (typeof process !== \"undefined\" && process.env?.NODE_ENV !== \"production\" && isIconOnly && !props[\"aria-label\"]) {\n    console.warn(\n      \"[Button] Icon-only buttons must have an `aria-label` describing the action.\",\n    );\n  }\n\n  const content = loading ? (\n    <Spinner size=\"sm\" />\n  ) : (\n    <>\n      {icon && iconPosition === \"left\" && icon}\n      {children}\n      {icon && iconPosition === \"right\" && icon}\n    </>\n  );\n\n  const typeValue: React.ButtonHTMLAttributes<HTMLButtonElement>[\"type\"] =\n    render ? undefined : \"button\";\n\n  const defaultProps = {\n    className: cn(\n      buttonVariants({ size: effectiveSize, variant, shape }),\n      iconOnly,\n      className,\n    ),\n    \"data-slot\": \"button\",\n    type: typeValue,\n    disabled: isDisabled || loading,\n    children: content,\n  };\n\n  return useRender({\n    defaultTagName: \"button\",\n    props: mergeProps<\"button\">(defaultProps, props),\n    render,\n  });\n}\n",
      "type": "registry:ui",
      "target": "components/ui/button.tsx"
    }
  ],
  "type": "registry:ui"
}