{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "button",
  "type": "registry:ui",
  "title": "Button",
  "description": "Action trigger: rounded rectangle, press feedback, stable loading state; default is the black primary of the app surface.",
  "dependencies": [
    "radix-ui",
    "class-variance-authority",
    "lucide-react"
  ],
  "registryDependencies": [
    "@wibo/utils"
  ],
  "files": [
    {
      "path": "src/components/button.tsx",
      "type": "registry:ui",
      "content": "import type { ComponentProps } from \"react\";\nimport { Slot } from \"radix-ui\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { LoaderCircle } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\n\n/*\n * Button recipe: a rounded rectangle (`rounded-control`, 8 px) at 28, 34 and\n * 36 px, 13 px medium label. Hover fills fade in over 120 ms and the button\n * settles to 97 % while pressed (`press`). Filled variants take an opaque\n * hover; the ghost is muted and turns to ink on hover.\n */\nconst buttonVariants = cva(\n  \"press relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-control text-label font-medium whitespace-nowrap outline-none select-none focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[loading=true]:disabled:opacity-100 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-primary text-primary-foreground hover:bg-primary-hover\",\n        secondary: \"bg-secondary text-secondary-foreground hover:bg-foreground/9\",\n        outline:\n          \"border border-input bg-card text-foreground shadow-card hover:bg-muted/60 aria-expanded:bg-muted/60 [&_svg]:text-muted-foreground hover:[&_svg]:text-foreground\",\n        ghost:\n          \"text-muted-foreground hover:bg-foreground/4 hover:text-foreground active:bg-foreground/6 aria-expanded:bg-foreground/6 aria-expanded:text-foreground\",\n        destructive: \"bg-danger text-danger-foreground hover:bg-danger/90 focus-visible:ring-danger/40\",\n        link: \"text-accent underline-offset-4 hover:underline\",\n      },\n      size: {\n        sm: \"h-7 gap-1.5 px-2.5 [&_svg:not([class*='size-'])]:size-3.5\",\n        md: \"h-8.5 px-3.5\",\n        lg: \"h-9 px-4\",\n        icon: \"press-icon size-8.5\",\n        \"icon-sm\": \"press-icon size-7 [&_svg:not([class*='size-'])]:size-3.5\",\n      },\n    },\n    compoundVariants: [{ variant: \"link\", className: \"h-auto rounded-sm px-0\" }],\n    defaultVariants: {\n      variant: \"default\",\n      size: \"md\",\n    },\n  },\n);\n\ntype ButtonProps = ComponentProps<\"button\"> &\n  VariantProps<typeof buttonVariants> & {\n    /** Render the child element (for example a router link) with button styles. */\n    asChild?: boolean;\n    /**\n     * Working: a spinner covers the label, the button keeps its width, is\n     * disabled and marked busy. It does not dim: a working action is not an\n     * unavailable one. Ignored with `asChild`.\n     */\n    loading?: boolean;\n  };\n\n/** Action trigger. `default` is the black primary of the app surface. */\nfunction Button({\n  className,\n  variant,\n  size,\n  asChild = false,\n  loading = false,\n  disabled,\n  children,\n  ...props\n}: ButtonProps) {\n  const resolvedVariant = variant ?? \"default\";\n  const resolvedSize = size ?? \"md\";\n  if (asChild) {\n    return (\n      <Slot.Root\n        data-slot=\"button\"\n        data-variant={resolvedVariant}\n        data-size={resolvedSize}\n        className={cn(buttonVariants({ variant, size, className }))}\n        {...props}\n      >\n        {children}\n      </Slot.Root>\n    );\n  }\n  return (\n    <button\n      data-slot=\"button\"\n      data-variant={resolvedVariant}\n      data-size={resolvedSize}\n      data-loading={loading || undefined}\n      className={cn(buttonVariants({ variant, size, className }))}\n      disabled={disabled || loading}\n      aria-busy={loading || undefined}\n      {...props}\n    >\n      {loading ? (\n        <>\n          <span aria-hidden=\"true\" className=\"absolute inset-0 flex items-center justify-center\">\n            <LoaderCircle className=\"animate-spin\" />\n          </span>\n          {/* The label keeps the button's width and its accessible name. */}\n          <span className=\"inline-flex items-center gap-[inherit] opacity-0\">{children}</span>\n        </>\n      ) : (\n        children\n      )}\n    </button>\n  );\n}\n\nexport { Button, buttonVariants };\nexport type { ButtonProps };\n"
    }
  ]
}
