{
  "$schema": "https://stalk-ui.com/schema/v1/registry-item.json",
  "name": "button",
  "type": "registry:ui",
  "dependencies": ["@radix-ui/react-slot", "@stalk-ui/preset"],
  "registryDependencies": ["spinner"],
  "files": [
    {
      "path": "src/components/ui/button.tsx",
      "type": "registry:ui",
      "content": "'use client'\n\nimport { Slot } from '@radix-ui/react-slot'\n// Stalk UI component - requires PandaCSS setup and @stalk-ui/preset.\nimport { Children, forwardRef } from 'react'\nimport { css, cx } from 'styled-system/css'\nimport { VisuallyHidden } from 'styled-system/jsx'\nimport { button as buttonRecipe } from 'styled-system/recipes'\n\nimport { Spinner, type SpinnerSize } from './spinner'\n\nimport type { Tone } from './tones'\nimport type { ButtonHTMLAttributes, MouseEvent, ReactNode } from 'react'\n\nexport type ButtonVariant = (typeof buttonRecipe.variantMap.variant)[number]\nexport type ButtonSize = (typeof buttonRecipe.variantMap.size)[number]\nexport type ButtonTone = Tone\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n  asChild?: boolean\n  loading?: boolean\n  /**\n   * Accessible label announced while `loading`. Defaults to `\"Loading\"`. Pass a\n   * localized string when the surrounding app supplies translations.\n   */\n  loadingLabel?: string\n  size?: ButtonSize\n  tone?: ButtonTone\n  variant?: ButtonVariant\n}\n\nconst SPINNER_SIZE_BY_BUTTON: Record<ButtonSize, SpinnerSize> = {\n  sm: 'sm',\n  md: 'md',\n  lg: 'lg',\n}\n\n// Wrap raw text/number children in a span so they become element siblings.\n// Without this, `<Button><Icon/>Save</Button>` lets the recipe's\n// `:has(> svg:only-child)` selector incorrectly match the icon (text nodes are\n// invisible to `:only-child`) and square the button.\nconst wrapTextChildren = (children: ReactNode): ReactNode =>\n  Children.map(children, (child) =>\n    typeof child === 'string' || typeof child === 'number' ? <span>{child}</span> : child,\n  )\n\nexport const Button = /* @__PURE__ */ forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n  {\n    asChild = false,\n    children,\n    className,\n    disabled,\n    loading = false,\n    loadingLabel = 'Loading',\n    onClick,\n    size = 'md',\n    tone = 'accent',\n    type = 'button',\n    variant = 'solid',\n    ...props\n  },\n  ref,\n) {\n  const isDisabled = disabled === true || loading\n  const Component = asChild ? Slot : 'button'\n\n  const handleClick = (event: MouseEvent<HTMLButtonElement>) => {\n    if (isDisabled) {\n      event.preventDefault()\n      event.stopPropagation()\n      return\n    }\n    onClick?.(event)\n  }\n\n  // `Slot` takes a single child element to clone, so we can't wrap the\n  // spinner overlay around a slotted child. When `asChild` is true we pass\n  // children through unchanged and lean on `aria-busy` + `data-loading` for\n  // state; the consumer's element keeps its native rendering.\n  const wrappedChildren = asChild ? children : wrapTextChildren(children)\n\n  const content =\n    loading && !asChild ? (\n      <>\n        <span\n          aria-hidden=\"true\"\n          className={css({\n            display: 'contents',\n            visibility: 'hidden',\n          })}\n        >\n          {wrappedChildren}\n        </span>\n        <VisuallyHidden>{loadingLabel}</VisuallyHidden>\n        <span\n          aria-hidden=\"true\"\n          className={css({\n            alignItems: 'center',\n            display: 'inline-flex',\n            inset: '0',\n            justifyContent: 'center',\n            pointerEvents: 'none',\n            pos: 'absolute',\n          })}\n        >\n          <Spinner size={SPINNER_SIZE_BY_BUTTON[size]} />\n        </span>\n      </>\n    ) : (\n      wrappedChildren\n    )\n\n  return (\n    <Component\n      ref={ref}\n      aria-busy={loading || undefined}\n      aria-disabled={asChild && isDisabled ? true : undefined}\n      className={cx(buttonRecipe({ size, variant }), css({ colorPalette: tone }), className)}\n      data-loading={loading ? '' : undefined}\n      disabled={asChild ? undefined : isDisabled}\n      onClick={handleClick}\n      type={asChild ? undefined : type}\n      {...props}\n    >\n      {content}\n    </Component>\n  )\n})\n"
    }
  ],
  "stalk": {
    "schemaVersion": "1.0",
    "preset": {
      "semanticTokens": {},
      "recipes": ["button"]
    },
    "packageDependencies": {
      "preset": "@stalk-ui/preset"
    },
    "pandaCodegen": true,
    "importAliases": {
      "styledSystem": "styled-system"
    }
  }
}
