{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "button",
  "type": "registry:ui",
  "title": "Button",
  "description": "Accessible action button with size, variant, and semantic tone support.",
  "dependencies": [
    "@base-ui/react@^1.7.0"
  ],
  "registryDependencies": [
    "https://control-ui.dev/r/core.json"
  ],
  "files": [
    {
      "path": "src/registry/knob-contracts/button-knobs.ts",
      "target": "@components/control-ui/knob-contracts/button-knobs.ts",
      "type": "registry:component",
      "content": "// Generated from src/registry/sources/control-ui/recipes/button.css by scripts/gen-knob-contracts.ts — run `bun run sync:knobs`.\nexport const buttonKnobs = [\n  \"--cui-button-radius\",\n  \"--cui-button-gap\",\n  \"--cui-button-icon\",\n  \"--cui-button-bg\",\n  \"--cui-button-bg-image\",\n  \"--cui-button-foreground\",\n  \"--cui-button-hover-bg\",\n  \"--cui-button-hover-foreground\",\n  \"--cui-button-press-bg\",\n  \"--cui-button-press-scale\",\n  \"--cui-button-active-bg\",\n  \"--cui-button-active-foreground\",\n  \"--cui-button-active-hover-bg\",\n  \"--cui-button-shadow\",\n  \"--cui-button-hover-shadow\",\n  \"--cui-button-press-shadow\",\n  \"--cui-button-active-shadow\",\n] as const;\nexport type ButtonKnobStyle = Partial<Record<(typeof buttonKnobs)[number], string>>;\n"
    },
    {
      "path": "src/registry/sources/control-ui/recipes/button.css",
      "target": "@components/control-ui/styles/recipes/button.css",
      "type": "registry:file",
      "content": "@layer components {\n  :where([data-control-family=\"button\"][data-control=\"true\"]) {\n    --cui-button-radius: var(--radius-control);\n    --cui-button-gap: 0.375rem;\n    --cui-button-icon: 1rem;\n    --cui-button-bg: transparent;\n    --cui-button-bg-image: none;\n    --cui-button-foreground: var(--muted-foreground);\n    --cui-button-hover-bg: oklch(from var(--foreground) l c h / 0.06);\n    --cui-button-hover-foreground: var(--foreground);\n    --cui-button-press-bg: var(--cui-button-hover-bg);\n    --cui-button-press-scale: 0.98;\n    --cui-button-active-bg: oklch(from var(--foreground) l c h / 0.08);\n    --cui-button-active-foreground: var(--foreground);\n    --cui-button-active-hover-bg: var(--cui-button-active-bg);\n    --cui-button-active-shadow: var(--cui-button-shadow);\n    --cui-button-shadow: 0 0 transparent;\n    --cui-button-hover-shadow: var(--cui-button-shadow);\n    --cui-button-press-shadow: var(--cui-button-hover-shadow);\n    border-radius: var(--cui-button-radius);\n    background-color: var(--cui-button-bg);\n    background-image: var(--cui-button-bg-image);\n    box-shadow: var(--cui-button-shadow);\n    color: var(--cui-button-foreground);\n    cursor: pointer;\n    font-weight: 500;\n    outline: none;\n    transition-property: color, background-color, border-color, box-shadow, scale, translate, opacity, filter, backdrop-filter;\n    transition-timing-function: var(--ease-standard);\n    transition-duration: var(--duration-fast);\n  }\n\n  /* Keyed on the family alone so every trigger owns the knob, and declared here so a skin re-values gap\n   * without losing to the size utility that Tailwind emits from a later cascade layer. */\n  :where([data-control-family=\"button\"]) {\n    gap: var(--cui-button-gap);\n  }\n\n  :where([data-control-family=\"button\"][data-size=\"xs\"]) {\n    --cui-button-gap: 0.25rem;\n    --cui-button-icon: 0.875rem;\n  }\n  :where([data-control-family=\"button\"][data-size=\"lg\"]) {\n    --cui-button-gap: 0.5rem;\n    --cui-button-icon: 1.125rem;\n  }\n\n  /* Default clamp for unclassed icons (lucide ships width/height 24); zero specificity, so any utility on the svg wins. */\n  :where([data-control-family=\"button\"][data-control=\"true\"] svg) {\n    pointer-events: none;\n    flex-shrink: 0;\n    width: var(--cui-button-icon);\n    height: var(--cui-button-icon);\n  }\n\n  :where([data-control-family=\"button\"][data-cursor=\"default\"]) {\n    cursor: default;\n  }\n\n  :where([data-control-family=\"button\"][data-variant=\"ghost\"]) {\n    --cui-button-foreground: var(--foreground);\n    --cui-button-active-bg: transparent;\n    --cui-button-active-foreground: var(--foreground);\n    --cui-button-active-hover-bg: var(--cui-button-hover-bg);\n  }\n\n  :where([data-control-family=\"button\"][data-variant=\"solid\"]) {\n    --cui-button-bg: var(--primary);\n    --cui-button-foreground: var(--primary-foreground);\n    --cui-button-hover-bg: oklch(from var(--primary) l c h / 0.9);\n    --cui-button-hover-foreground: var(--primary-foreground);\n    --cui-button-shadow: var(--shadow-sm);\n    --cui-button-active-bg: var(--cui-button-bg);\n    --cui-button-active-foreground: var(--cui-button-foreground);\n    --cui-button-active-hover-bg: var(--cui-button-hover-bg);\n  }\n\n  :where([data-control-family=\"button\"][data-variant=\"surface\"]) {\n    --cui-button-bg: oklch(from var(--card) l c h / 0.72);\n    --cui-button-foreground: var(--foreground);\n    --cui-button-hover-bg: oklch(from var(--foreground) l c h / 0.05);\n    --cui-button-hover-foreground: var(--foreground);\n    --cui-button-shadow: inset 0 0 0 1px var(--border), var(--shadow-sm);\n    --cui-button-active-bg: oklch(from var(--primary) l c h / 0.08);\n    --cui-button-active-foreground: var(--primary-text);\n    --cui-button-active-shadow: inset 0 0 0 1px oklch(from var(--primary) l c h / 0.6), var(--shadow-sm);\n    --cui-button-active-hover-bg: oklch(from var(--primary) l c h / 0.1);\n  }\n\n  :where([data-control-family=\"button\"]:is([data-variant=\"surface\"], [data-variant=\"ghost\"], [data-variant=\"quiet\"])[data-popup-open]) {\n    background-color: oklch(from var(--foreground) l c h / 0.07);\n    color: var(--cui-button-hover-foreground);\n  }\n\n  :where([data-control-family=\"button\"][data-tone=\"primary\"]:not([data-variant=\"solid\"])) {\n    --cui-button-foreground: var(--primary-text);\n    --cui-button-hover-foreground: var(--primary-text);\n  }\n\n  :where([data-control-family=\"button\"][data-tone=\"danger\"][data-variant=\"solid\"]) {\n    --cui-button-bg: var(--destructive);\n    --cui-button-foreground: var(--destructive-foreground);\n    --cui-button-hover-bg: oklch(from var(--destructive) l c h / 0.9);\n    --cui-button-hover-foreground: var(--destructive-foreground);\n    --cui-button-active-bg: var(--cui-button-bg);\n    --cui-button-active-foreground: var(--cui-button-foreground);\n    --cui-button-active-hover-bg: var(--cui-button-hover-bg);\n  }\n\n  :where([data-control-family=\"button\"][data-tone=\"danger\"]:not([data-variant=\"solid\"])) {\n    --cui-button-foreground: var(--destructive-text);\n    --cui-button-hover-foreground: var(--destructive-text);\n  }\n\n  :where([data-control-family=\"button\"][data-shape=\"circle\"]) {\n    --cui-button-radius: 9999px;\n  }\n\n  @media (hover: hover) {\n    :where([data-control-family=\"button\"][data-control=\"true\"]:hover) {\n      background-color: var(--cui-button-hover-bg);\n      box-shadow: var(--cui-button-hover-shadow);\n      color: var(--cui-button-hover-foreground);\n    }\n  }\n\n  :where([data-control-family=\"button\"][data-active=\"true\"]) {\n    background-color: var(--cui-button-active-bg);\n    box-shadow: var(--cui-button-active-shadow);\n    color: var(--cui-button-active-foreground);\n  }\n\n  @media (hover: hover) {\n    :where([data-control-family=\"button\"][data-active=\"true\"]:hover) {\n      background-color: var(--cui-button-active-hover-bg);\n      color: var(--cui-button-active-foreground);\n    }\n  }\n\n  :where([data-control-family=\"button\"][data-control=\"true\"]:active) {\n    background-color: var(--cui-button-press-bg);\n    box-shadow: var(--cui-button-press-shadow);\n    scale: var(--cui-button-press-scale);\n  }\n\n  :where([data-control-family=\"button\"]:is(:disabled, [data-disabled], [aria-disabled=\"true\"])) {\n    cursor: not-allowed;\n    opacity: 0.45;\n  }\n\n  :where([data-control-family=\"button\"][data-button-kind=\"model-switcher\"][data-slot=\"indicator\"]) {\n    border-radius: calc(infinity * 1px);\n    background-color: var(--primary);\n  }\n}\n\n@property --cui-button-radius {\n  syntax: \"<length-percentage>\";\n  inherits: true;\n  initial-value: 0px;\n}\n\n@property --cui-button-gap {\n  syntax: \"<length-percentage>\";\n  inherits: true;\n  initial-value: 0px;\n}\n\n@property --cui-button-icon {\n  syntax: \"<length>\";\n  inherits: true;\n  initial-value: 16px;\n}\n\n@property --cui-button-bg {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-button-bg-image {\n  syntax: \"*\";\n  inherits: true;\n}\n\n@property --cui-button-foreground {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-button-hover-bg {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-button-hover-foreground {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-button-press-bg {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-button-press-scale {\n  syntax: \"<number>\";\n  inherits: true;\n  initial-value: 1;\n}\n\n@property --cui-button-active-bg {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-button-active-foreground {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-button-active-hover-bg {\n  syntax: \"<color>\";\n  inherits: true;\n  initial-value: transparent;\n}\n\n@property --cui-button-shadow {\n  syntax: \"*\";\n  inherits: true;\n}\n\n@property --cui-button-hover-shadow {\n  syntax: \"*\";\n  inherits: true;\n  initial-value: none;\n}\n\n@property --cui-button-press-shadow {\n  syntax: \"*\";\n  inherits: true;\n  initial-value: none;\n}\n\n@property --cui-button-active-shadow {\n  syntax: \"*\";\n  inherits: true;\n}\n"
    },
    {
      "path": "src/registry/sources/control-ui/ui/button.tsx",
      "target": "@components/control-ui/ui/button.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport { Button as BaseButton } from \"@base-ui/react/button\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport type { ComponentProps, CSSProperties, ReactNode } from \"react\";\nimport type { RenderProp } from \"@/components/control-ui/control-props\";\nimport type { ControlSize, ControlTone, ControlVariant } from \"@/components/control-ui/control-variants\";\nimport { buttonGapClass, controlSize } from \"@/components/control-ui/control-variants\";\nimport type { ButtonKnobStyle } from \"@/components/control-ui/knob-contracts/button-knobs\";\nimport { cn } from \"@/components/control-ui/lib/cn\";\nimport { skinAdornment } from \"@/components/control-ui/skin\";\n\nexport type ButtonVariant = ControlVariant;\n\nexport type ButtonSize = ControlSize;\n\nexport type ButtonTone = ControlTone;\n\nexport const buttonShapes = [\"default\", \"circle\"] as const;\n\nexport type ButtonShape = (typeof buttonShapes)[number];\n\nexport type ButtonAppearanceProps = {\n  variant?: ButtonVariant;\n  size?: ButtonSize;\n  tone?: ButtonTone;\n  active?: boolean;\n  iconOnly?: boolean;\n  shape?: ButtonShape;\n  style?: CSSProperties & ButtonKnobStyle;\n};\n\nexport type ButtonProps = ComponentProps<\"button\"> &\n  ButtonAppearanceProps & {\n    render?: RenderProp<ComponentProps<\"button\">, { disabled: boolean }>;\n    nativeButton?: boolean;\n  };\n\nexport type ButtonLinkProps = ComponentProps<\"a\"> &\n  ButtonAppearanceProps & {\n    render?: RenderProp<ComponentProps<\"a\">>;\n  };\n\nexport type ButtonLabelProps = ComponentProps<\"label\"> & ButtonAppearanceProps;\n\nconst buttonStructureClasses = \"relative isolate inline-flex shrink-0 items-center justify-center overflow-visible whitespace-nowrap\";\n\nexport const buttonContentClasses = \"relative z-[1] inline-flex min-w-0 items-center justify-center gap-[inherit]\";\n\nexport function buttonRecipeClasses(size: ControlSize, iconOnly = false): string {\n  return cn(buttonStructureClasses, controlSize({ size }), buttonGapClass, iconOnly && \"aspect-square px-0\");\n}\n\nfunction ButtonContent({ children }: { children: ReactNode }) {\n  return (\n    <span data-control-ui=\"button\" data-control-family=\"button\" data-slot=\"content\" className={buttonContentClasses}>\n      {children}\n    </span>\n  );\n}\n\n/** The content wrapper only earns its node when a skin layer sits behind it and needs stacking above it. */\nfunction ButtonBody({ layer, wrap = true, children }: { layer: ReactNode; wrap?: boolean; children: ReactNode }) {\n  return (\n    <>\n      {layer}\n      {wrap && layer ? <ButtonContent>{children}</ButtonContent> : children}\n    </>\n  );\n}\n\n// composition flows through Base UI Button's `render` prop\nexport function Button({\n  variant = \"quiet\",\n  size = \"sm\",\n  tone = \"neutral\",\n  active = false,\n  iconOnly = false,\n  shape = \"default\",\n  type = \"button\",\n  disabled,\n  render,\n  nativeButton,\n  className,\n  children,\n  ...props\n}: ButtonProps) {\n  const layer = skinAdornment(\"button\", \"layer\", { variant, tone });\n\n  return (\n    <BaseButton\n      type={nativeButton === false ? undefined : type}\n      disabled={disabled}\n      data-control-ui=\"button\"\n      data-control-family=\"button\"\n      data-slot=\"root\"\n      data-control=\"true\"\n      data-active={active ? \"true\" : undefined}\n      data-icon-only={iconOnly ? \"true\" : undefined}\n      data-shape={shape}\n      data-variant={variant}\n      data-tone={tone}\n      data-size={size}\n      className={cn(buttonRecipeClasses(size, iconOnly), className)}\n      render={render}\n      nativeButton={nativeButton}\n      {...props}\n    >\n      <ButtonBody layer={layer} wrap={!render}>\n        {children}\n      </ButtonBody>\n    </BaseButton>\n  );\n}\n\nexport function ButtonLink({\n  variant = \"quiet\",\n  size = \"sm\",\n  tone = \"neutral\",\n  active = false,\n  iconOnly = false,\n  shape = \"default\",\n  render,\n  className,\n  children,\n  ...props\n}: ButtonLinkProps) {\n  return useRender({\n    defaultTagName: \"a\",\n    render,\n    props: {\n      ...props,\n      \"data-control-ui\": \"button\",\n      \"data-control-family\": \"button\",\n      \"data-slot\": \"root\",\n      \"data-control\": \"true\",\n      \"data-active\": active ? \"true\" : undefined,\n      \"data-icon-only\": iconOnly ? \"true\" : undefined,\n      \"data-shape\": shape,\n      \"data-variant\": variant,\n      \"data-tone\": tone,\n      \"data-size\": size,\n      className: cn(buttonRecipeClasses(size, iconOnly), className),\n      children: <ButtonBody layer={skinAdornment(\"button\", \"layer\", { variant, tone })}>{children}</ButtonBody>,\n    },\n  });\n}\n\nexport function ButtonLabel({\n  variant = \"quiet\",\n  size = \"sm\",\n  tone = \"neutral\",\n  active = false,\n  iconOnly = false,\n  shape = \"default\",\n  className,\n  children,\n  ...props\n}: ButtonLabelProps) {\n  return (\n    // biome-ignore lint/a11y/noLabelWithoutControl: The wrapped file input is supplied through children.\n    <label\n      {...props}\n      data-control-ui=\"button\"\n      data-control-family=\"button\"\n      data-slot=\"root\"\n      data-control=\"true\"\n      data-active={active ? \"true\" : undefined}\n      data-icon-only={iconOnly ? \"true\" : undefined}\n      data-shape={shape}\n      data-variant={variant}\n      data-tone={tone}\n      data-size={size}\n      className={cn(buttonRecipeClasses(size, iconOnly), className)}\n    >\n      <ButtonBody layer={skinAdornment(\"button\", \"layer\", { variant, tone })}>{children}</ButtonBody>\n    </label>\n  );\n}\n"
    }
  ],
  "css": {
    "@import \"../components/control-ui/styles/recipes/button.css\"": {}
  },
  "meta": {}
}
