{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "button",
  "title": "Button",
  "description": "Tactile buttons with loading state, keyboard hints and six variants.",
  "dependencies": [
    "class-variance-authority",
    "radix-ui"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.cuelark.com/r/theme.json",
    "https://ui.cuelark.com/r/motion.json"
  ],
  "files": [
    {
      "path": "src/registry/cuelark/ui/button.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { Slot } from \"radix-ui\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cuePing } from \"@/lib/motion\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * The fill lives on a `::before` surface layer: it compresses slightly on press while the label stays put and\n * sharp, so the press reads as physical without the text wobbling. Every color comes from theme tokens.\n */\nconst buttonVariants = cva(\n  [\n    \"group/button relative isolate inline-flex shrink-0 items-center justify-center gap-1.5 overflow-hidden rounded-lg text-sm font-medium whitespace-nowrap select-none outline-none\",\n    \"transition-[color,box-shadow] duration-150 ease-cue focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-busy:cursor-progress aria-invalid:ring-3 aria-invalid:ring-destructive/25\",\n    \"before:pointer-events-none before:absolute before:inset-0 before:-z-10 before:rounded-[inherit] before:transition-[scale,background-color,box-shadow] before:duration-200 before:ease-cue\",\n    \"active:not-aria-[haspopup]:before:scale-[0.97] motion-reduce:before:transition-none motion-reduce:active:before:scale-100\",\n    \"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n    \"[&_[data-slot=kbd]]:border-current/15 [&_[data-slot=kbd]]:bg-current/8 [&_[data-slot=kbd]]:text-current/70 [&_[data-slot=kbd]]:shadow-none\",\n  ],\n  {\n    variants: {\n      variant: {\n        primary:\n          \"text-primary-foreground before:bg-primary before:shadow-[inset_0_1px_0_var(--cl-highlight),0_0_0_1px_color-mix(in_oklch,var(--primary),var(--foreground)_12%),0_1px_2px_var(--cl-shade)] hover:before:bg-[color-mix(in_oklch,var(--primary),var(--background)_12%)]\",\n        secondary:\n          \"text-secondary-foreground before:bg-secondary hover:before:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_6%)] aria-expanded:before:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_6%)]\",\n        outline:\n          \"text-foreground before:bg-background before:shadow-[0_0_0_1px_var(--input),0_1px_2px_var(--cl-shade)] hover:before:bg-muted aria-expanded:before:bg-muted dark:before:bg-input/30 dark:hover:before:bg-input/50\",\n        ghost: \"text-foreground hover:before:bg-foreground/6 aria-expanded:before:bg-foreground/6 active:before:bg-foreground/10\",\n        destructive:\n          \"text-white before:bg-destructive before:shadow-[inset_0_1px_0_rgb(255_255_255/0.16),0_1px_2px_var(--cl-shade)] hover:before:bg-[color-mix(in_oklch,var(--destructive),black_8%)] focus-visible:ring-destructive/40 dark:before:bg-destructive/80\",\n        link: \"text-foreground underline decoration-border underline-offset-4 hover:decoration-foreground\",\n      },\n      size: {\n        xs: \"h-ctl-xs gap-1 rounded-md px-2 text-xs [&_svg:not([class*='size-'])]:size-3.5\",\n        sm: \"h-ctl-sm gap-1 rounded-md px-2.5 text-[0.8125rem] [&_svg:not([class*='size-'])]:size-3.5\",\n        md: \"h-ctl-md px-3\",\n        lg: \"h-ctl-lg gap-2 px-4\",\n        icon: \"size-ctl-md\",\n        \"icon-sm\": \"size-ctl-sm rounded-md\",\n        \"icon-lg\": \"size-ctl-lg\",\n      },\n      pill: {\n        true: \"rounded-full\",\n      },\n    },\n    compoundVariants: [\n      { variant: \"link\", className: \"h-auto px-0\" },\n      { pill: true, size: \"md\", className: \"px-4\" },\n      { pill: true, size: \"lg\", className: \"px-5\" },\n    ],\n    defaultVariants: { variant: \"primary\", size: \"md\" },\n  },\n);\n\ntype ButtonProps = React.ComponentProps<\"button\"> &\n  VariantProps<typeof buttonVariants> & {\n    /** Render the child element (e.g. a link) with button styles instead of a `<button>`. */\n    asChild?: boolean;\n    /** Shows a spinner, keeps the button's width, sets `aria-busy` and blocks clicks. */\n    loading?: boolean;\n    /** A keyboard shortcut hint rendered inside the button, e.g. `\"⌘K\"`. */\n    kbd?: React.ReactNode;\n    /** The ring that ripples out from the press point (GSAP, follows `--cl-motion`). */\n    ping?: boolean;\n  };\n\nfunction Button({ className, variant = \"primary\", size = \"md\", pill, asChild = false, loading = false, kbd, ping = variant !== \"link\", disabled, children, onPointerDown, onClick, ...props }: ButtonProps) {\n  const shared = {\n    \"data-slot\": \"button\",\n    \"data-variant\": variant,\n    \"data-size\": size,\n    className: cn(buttonVariants({ variant, size, pill }), className),\n    onPointerDown: (e: React.PointerEvent<HTMLButtonElement>) => {\n      onPointerDown?.(e);\n      if (ping && !e.defaultPrevented) cuePing(e.currentTarget, e);\n    },\n    onClick: (e: React.MouseEvent<HTMLButtonElement>) => {\n      onClick?.(e);\n      // Keyboard activation (Enter/Space) reports detail 0: ping from the center.\n      if (ping && e.detail === 0 && !e.defaultPrevented) cuePing(e.currentTarget, { clientX: 0, clientY: 0 });\n    },\n  };\n\n  if (asChild) {\n    return (\n      <Slot.Root {...shared} {...props}>\n        {children}\n      </Slot.Root>\n    );\n  }\n\n  return (\n    <button {...shared} aria-busy={loading || undefined} disabled={disabled || loading} {...props}>\n      {loading && <Spinner className=\"absolute inset-0 m-auto\" />}\n      <span data-slot=\"button-content\" className={cn(\"inline-flex items-center gap-[inherit]\", loading && \"invisible\")}>\n        {children}\n        {kbd != null && (\n          <kbd data-slot=\"kbd\" className=\"cue-kbd -me-1.5\">\n            {kbd}\n          </kbd>\n        )}\n      </span>\n    </button>\n  );\n}\n\nfunction Spinner({ className }: { className?: string }) {\n  return (\n    <svg data-slot=\"spinner\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\" className={cn(\"size-4 animate-spin motion-reduce:animate-[spin_1.5s_linear_infinite]\", className)}>\n      <circle cx=\"8\" cy=\"8\" r=\"6.25\" stroke=\"currentColor\" strokeOpacity=\"0.25\" strokeWidth=\"1.5\" />\n      <path d=\"M14.25 8A6.25 6.25 0 0 0 8 1.75\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n    </svg>\n  );\n}\n\nexport { Button, buttonVariants };\nexport type { ButtonProps };\n",
      "type": "registry:ui"
    }
  ],
  "docs": "Docs and examples: https://ui.cuelark.com/components/button",
  "categories": [
    "actions"
  ],
  "type": "registry:ui"
}