# Button

- Page: <https://kznlabs.github.io/labsUI/components/button>

## Button (`button`, component)

Five variants, four sizes, spring press feedback, an animated loading spinner and in-place success state, a two-step confirm that swaps the label on first click, and a configurable swap direction. Width stays fixed to the longest label.

- Install: `npx @kznlabs/labsui add button`
- Docs: <https://kznlabs.github.io/labsUI/components/button>
- Brings: button-variants, utils
- npm: clsx@^2.1.1, lucide-react@1.63.0, motion@14.7.1, tailwind-merge@^3.7.0

### API

- `<Button>`
  - `children?: ReactNode`: Label; its width is reserved so loading and success never resize the button.
  - `confirmLabel?: ReactNode`: Two-step action: first click swaps the label to this, second click runs `onClick`.
  - `confirmTimeout?: number` = `3000`: Ms before an armed confirm step resets. Also resets on blur or Esc.
  - `loading?: boolean` = `false`: Disables the button and shows a spinner before the label.
  - `loadingLabel?: ReactNode`: Label shown while loading, with the spinner. Defaults to `children`.
  - `size?: "default" | "sm" | "lg" | "icon"`: Height and padding; "icon" is square, so give it an `aria-label`.
  - `status?: ButtonStatus` = `"idle"`: In-place feedback: `loading` spins, `success` swaps the label to `successLabel`.
  - `successLabel?: ReactNode`: Text shown on `status="success"`, with a check icon. Defaults to "Done".
  - `swap?: SwapDirection` = `"left"`: Where the old label slides out to on a swap. Default "left".
  - `variant?: "default" | "secondary" | "outline" | "ghost" | "destructive"`: Fill and emphasis: one primary action per view, `destructive` for irreversible ones.

- `type ButtonStatus = "idle" | "loading" | "success"`
- `type SwapDirection = "left" | "right" | "up" | "down"`

### Usage

```tsx
<Button variant="destructive" confirmLabel="Yes, delete my account" onClick={remove}>Delete account</Button>
```

### Example: save-button.tsx

```tsx
import { Button } from "@/components/ui/button";
export function SaveButton({
  saving,
  saved,
}: {
  saving: boolean;
  saved: boolean;
}) {
  return (
    <Button
      variant="outline"
      status={saving ? "loading" : saved ? "success" : "idle"}
      loadingLabel="Saving…"
      successLabel="Saved!"
    >
      Save
    </Button>
  );
}
```

### Example: delete-account.tsx

```tsx
import { Button } from "@/components/ui/button";
export function DeleteAccount({
  status,
  onDelete,
}: {
  status: "idle" | "loading" | "success";
  onDelete: () => Promise<void>;
}) {
  return (
    <Button
      variant="destructive"
      confirmLabel="Yes, delete my account"
      loadingLabel="Deleting…"
      successLabel="Account deleted"
      status={status}
      onClick={onDelete}
    >
      Delete account
    </Button>
  );
}
```
