{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "button",
  "type": "registry:ui",
  "title": "Button",
  "description": "Triggers an action. Variants solid, outline, soft, ghost and danger; isLoading shows a spinner and sets aria-busy.",
  "dependencies": [
    "@radix-ui/react-slot@^1.1.0",
    "tailwind-variants@^1.0.0"
  ],
  "registryDependencies": [
    "https://junco.madebytobi.com/r/junco-theme.json",
    "https://junco.madebytobi.com/r/spinner.json",
    "https://junco.madebytobi.com/r/utils.json"
  ],
  "files": [
    {
      "path": "registry/junco/ui/button.tsx",
      "type": "registry:ui",
      "content": "import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\nimport { cn } from \"@/lib/utils\";\nimport { Spinner } from \"@/components/ui/spinner\";\n\n/**\n * Reference component. Every Junco component should follow this shape:\n * - variants from tailwind-variants\n * - tokens only (no raw colors)\n * - className override, asChild for links\n * - accessible states (disabled, aria-busy)\n */\nexport const buttonVariants = tv({\n  base: [\n    \"inline-flex items-center justify-center gap-2 whitespace-nowrap select-none\",\n    \"rounded-control border text-sm font-medium\",\n    \"transition-colors duration-fast ease-standard\",\n    \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\",\n    \"disabled:pointer-events-none disabled:opacity-50\",\n  ],\n  variants: {\n    variant: {\n      solid: \"border-transparent bg-primary text-primary-foreground hover:bg-primary/90\",\n      outline: \"border-border bg-surface text-foreground hover:bg-accent\",\n      soft: \"border-transparent bg-secondary text-secondary-foreground hover:bg-accent\",\n      ghost: \"border-transparent bg-transparent text-foreground hover:bg-accent\",\n      danger: \"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n    },\n    size: {\n      sm: \"h-8 px-2.5 text-[13px]\",\n      md: \"h-9 px-3.5\",\n      lg: \"h-11 px-4.5 text-[15px]\",\n      icon: \"size-9 p-0\",\n    },\n  },\n  defaultVariants: { variant: \"solid\", size: \"md\" },\n});\n\nexport interface ButtonProps\n  extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n    VariantProps<typeof buttonVariants> {\n  /** Render as the child element (for example a link) instead of a button. */\n  asChild?: boolean;\n  /** Shows a spinner, disables the button and sets aria-busy. */\n  isLoading?: boolean;\n}\n\n// Bundlers replace process.env.NODE_ENV at build time; declared here so the package needs no Node types.\ndeclare const process: { env: { NODE_ENV?: string } } | undefined;\nconst isDev = typeof process !== \"undefined\" && process.env.NODE_ENV !== \"production\";\n\n/** Icon-only buttons have no visible text, so without a label screen readers announce just \"button\". */\nfunction useIconButtonLabelWarning(isIconOnly: boolean, props: React.ButtonHTMLAttributes<HTMLButtonElement>) {\n  const unlabeled = isIconOnly && !props[\"aria-label\"] && !props[\"aria-labelledby\"] && !props.title;\n  React.useEffect(() => {\n    if (isDev && unlabeled) console.warn('Junco Button: size=\"icon\" needs an aria-label (or aria-labelledby / title) so screen readers can name it.');\n  }, [unlabeled]);\n}\n\n/** Triggers an action. Variants solid, outline, soft, ghost and danger; isLoading shows a spinner and sets aria-busy. */\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n  ({ className, variant, size, asChild = false, isLoading = false, disabled, children, ...props }, ref) => {\n    const Comp = asChild ? Slot : \"button\";\n    useIconButtonLabelWarning(size === \"icon\" && !asChild, props);\n    return (\n      <Comp\n        ref={ref}\n        className={cn(buttonVariants({ variant, size }), className)}\n        disabled={asChild ? undefined : disabled || isLoading}\n        aria-busy={isLoading || undefined}\n        {...props}\n      >\n        {isLoading && !asChild ? (\n          <>\n            <Spinner label={null} className=\"size-3.5\" />\n            {children}\n          </>\n        ) : (\n          children\n        )}\n      </Comp>\n    );\n  },\n);\nButton.displayName = \"Button\";\n"
    }
  ],
  "docs": "Docs and examples: https://junco.madebytobi.com/docs/components/button",
  "meta": {
    "hash": "9a17e0a2a5524d12",
    "source": "packages/react/src/components/button.tsx",
    "exports": [
      "buttonVariants",
      "ButtonProps",
      "Button"
    ],
    "props": {
      "Button": {
        "extends": "React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants>",
        "props": [
          {
            "name": "asChild",
            "optional": true,
            "type": "boolean",
            "description": "Render as the child element (for example a link) instead of a button."
          },
          {
            "name": "isLoading",
            "optional": true,
            "type": "boolean",
            "description": "Shows a spinner, disables the button and sets aria-busy."
          }
        ]
      }
    },
    "variants": {
      "variants": {
        "variant": [
          "solid",
          "outline",
          "soft",
          "ghost",
          "danger"
        ],
        "size": [
          "sm",
          "md",
          "lg",
          "icon"
        ]
      },
      "defaultVariants": {
        "variant": "solid",
        "size": "md"
      }
    },
    "docsUrl": "https://junco.madebytobi.com/docs/components/button"
  }
}
