{
  "$schema": "https://blode.co/ui/schema/registry-item.json",
  "name": "button",
  "title": "Button",
  "author": "Matthew Blode",
  "description": "An interactive element that triggers an action when clicked.",
  "dependencies": ["@base-ui/react"],
  "registryDependencies": ["spinner"],
  "files": [
    {
      "path": "ui/button.tsx",
      "content": "import { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { cva } from \"class-variance-authority\";\nimport type { VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Spinner } from \"@/components/ui/spinner\";\n\nconst buttonVariants = cva(\n  \"group/button relative inline-flex shrink-0 select-none items-center justify-center whitespace-nowrap rounded-lg border border-transparent bg-clip-padding font-medium text-sm outline-none transition-[color,background-color,border-color,box-shadow,opacity,transform] duration-150 ease-out focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-45 aria-disabled:pointer-events-none aria-disabled:cursor-not-allowed aria-disabled:opacity-45 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n  {\n    defaultVariants: {\n      size: \"default\",\n      variant: \"default\",\n    },\n    variants: {\n      size: {\n        default:\n          \"h-10 gap-1.5 px-3 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5\",\n        icon: \"size-10\",\n        \"icon-lg\": \"size-11\",\n        \"icon-sm\":\n          \"size-9 in-data-[slot=button-group]:rounded-lg rounded-[min(var(--radius-md),12px)]\",\n        \"icon-xs\":\n          \"size-8 in-data-[slot=button-group]:rounded-lg rounded-[min(var(--radius-md),10px)] [&_svg:not([class*='size-'])]:size-3\",\n        input:\n          \"h-[var(--field-height)] gap-2 rounded-[var(--field-radius)] px-[var(--field-padding-x)] py-[var(--field-padding-y)]\",\n        \"input-sm\":\n          \"h-[var(--field-height-sm)] gap-2 rounded-[var(--field-radius)] px-[var(--field-padding-x)] py-[var(--field-padding-y)]\",\n        lg: \"h-11 gap-1.5 px-4 has-data-[icon=inline-end]:pr-3.5 has-data-[icon=inline-start]:pl-3.5\",\n        sm: \"h-9 gap-1 in-data-[slot=button-group]:rounded-lg rounded-[min(var(--radius-md),12px)] px-3 text-[0.8rem] has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3.5\",\n        xs: \"h-8 gap-1 in-data-[slot=button-group]:rounded-lg rounded-[min(var(--radius-md),10px)] px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3\",\n      },\n      variant: {\n        default:\n          \"bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/95 aria-pressed:bg-primary/95\",\n        destructive:\n          \"bg-destructive text-destructive-foreground hover:bg-destructive/90 focus-visible:border-destructive focus-visible:ring-destructive/30 active:bg-destructive/80 aria-pressed:bg-destructive/80\",\n        destructiveSecondary:\n          \"border-red-200 text-red-700 hover:bg-red-50 active:bg-red-100 aria-pressed:bg-red-100 dark:border-red-800 dark:text-red-300 dark:aria-pressed:bg-red-950 dark:active:bg-red-950 dark:hover:bg-red-950/60\",\n        ghost:\n          \"hover:bg-muted hover:text-foreground active:bg-muted/80 aria-expanded:bg-muted aria-pressed:bg-muted/80 dark:aria-pressed:bg-muted/60 dark:active:bg-muted/60 dark:hover:bg-muted/50\",\n        input:\n          \"border-input bg-card font-normal font-sans text-base text-foreground leading-snug shadow-input hover:border-input-hover focus-visible:ring-2 focus-visible:ring-ring/15 focus-visible:ring-offset-1 focus-visible:ring-offset-background active:border-input-hover/80 aria-pressed:border-input-hover aria-invalid:border-destructive-foreground data-[placeholder]:text-placeholder-foreground\",\n        link: \"text-primary underline-offset-4 hover:underline active:opacity-80 aria-pressed:underline aria-pressed:opacity-80\",\n        outline:\n          \"border-border bg-background hover:bg-muted hover:text-foreground active:bg-muted/80 aria-expanded:bg-muted aria-pressed:bg-muted/80 dark:border-input dark:bg-input/30 dark:aria-pressed:bg-input/60 dark:active:bg-input/60 dark:hover:bg-input/50\",\n        secondary:\n          \"bg-secondary text-secondary-foreground hover:bg-secondary/85 active:bg-secondary/75 aria-expanded:bg-secondary aria-pressed:bg-secondary/75\",\n        success:\n          \"bg-success text-success-foreground hover:bg-success/90 focus-visible:border-success focus-visible:ring-success/30 active:bg-success/80 aria-pressed:bg-success/80\",\n        successSecondary:\n          \"border-green-200 text-green-700 hover:bg-green-50 active:bg-green-100 aria-pressed:bg-green-100 dark:border-green-800 dark:text-green-300 dark:aria-pressed:bg-green-950 dark:active:bg-green-950 dark:hover:bg-green-950/60\",\n        warning:\n          \"bg-warning text-warning-foreground hover:bg-warning/90 focus-visible:border-warning focus-visible:ring-warning/30 active:bg-warning/80 aria-pressed:bg-warning/80\",\n        warningSecondary:\n          \"border-yellow-200 text-yellow-700 hover:bg-yellow-50 active:bg-yellow-100 aria-pressed:bg-yellow-100 dark:border-yellow-800 dark:text-yellow-300 dark:aria-pressed:bg-yellow-950 dark:active:bg-yellow-950 dark:hover:bg-yellow-950/60\",\n      },\n    },\n  },\n);\n\nconst Button = ({\n  className,\n  variant = \"default\",\n  size = \"default\",\n  asChild = false,\n  loading = false,\n  disabled,\n  children,\n  ...props\n}: React.ComponentProps<\"button\"> &\n  VariantProps<typeof buttonVariants> & {\n    asChild?: boolean;\n    loading?: boolean;\n  }) => {\n  const isDisabled = disabled || loading;\n  const useAsChild = asChild && !loading;\n\n  const renderedChildren = loading ? (\n    <>\n      <span aria-hidden=\"true\" className=\"invisible opacity-0\">\n        {children}\n      </span>\n      <span className=\"-translate-x-1/2 -translate-y-1/2 absolute top-1/2 left-1/2\">\n        <Spinner />\n      </span>\n    </>\n  ) : (\n    children\n  );\n\n  return useRender({\n    defaultTagName: \"button\",\n    props: mergeProps<\"button\">(\n      {\n        className: cn(buttonVariants({ className, size, variant }), loading && \"cursor-wait\"),\n      },\n      useAsChild\n        ? props\n        : {\n            ...props,\n            ...(loading && { \"aria-busy\": true }),\n            children: renderedChildren,\n            disabled: isDisabled,\n          },\n    ),\n    render: useAsChild ? (children as React.ReactElement) : undefined,\n    state: {\n      size,\n      slot: \"button\",\n      variant,\n    },\n  });\n};\n\nexport { Button, buttonVariants };\n",
      "type": "registry:ui",
      "target": ""
    }
  ],
  "type": "registry:ui"
}
