# Button

> The action control for both products: filled with `primary` by default, in six variants and six sizes (Max's `components/ui/button.tsx`).

## Facts

- **Product**: Max
- **Family**: Actions
- **Global**: `window.DigitalCrew.Button`
- **Source**: `max-agent: src/components/ui/button.tsx`
- **Import in the app**: `import { Button } from "@/components/ui/button";`
- **Live preview**: /design/components/button/preview.html
- **Page**: /design/components/button

## Guidelines

**Variants**

| variant | fill / text | use |
|---|---|---|
| `default` | `primary` / `primary-foreground`; hover 90% | The one main action per view. Ink in Crew OS, orange in Max. |
| `secondary` | `secondary` / `secondary-foreground`; hover 80% | Quiet filled actions beside a default one ("Save draft"). |
| `outline` | `background` + `border` + `shadow-xs`; dark: `input` at 30% | Cancel, toolbar actions, icon buttons on busy rows. |
| `ghost` | none; hover `accent` | Low-emphasis actions in dense UI: table rows, card footers. |
| `destructive` | `destructive` / white; dark themes at 60% | Irreversible actions only, behind a confirm. |
| `link` | `primary` text, underline on hover | Inline actions inside text. |

**Sizes**: `sm` 32px, `default` 36px, `lg` 40px; `icon-sm`, `icon`, `icon-lg` are squares of the same heights. `icon-sm` (32px) is the smallest target Max allows; use `icon` or larger on touch. Radius `radius-md`, text 14px medium.

**What you provide**: a verb-first label in sentence case ("Launch campaign", "Review drafts"); 16px Lucide icons as children (the button sizes and spaces them); `aria-label` on every icon-only button; `disabled` with a `Loader2` spinner and a "…" label while working ("Writing…"). Keep the changing label in its own element so browser translation doesn't break it.

**Weight follows cost**: only actions that spend money or send something get the filled `default`; AI actions carry a `Sparkles` icon and a spinner while the model writes, then drop back to `outline` once their content is on screen.

**Focus**: `ring` border plus a 3px halo at 50% (`focus-visible` only). The shared `@digitalcrew/ui` Button draws the halo at full strength and adds the **Glass** finish; any Button takes it with `data-crew-button-finish="glass"` and `className="relative isolate"`.

**Don't** put two `default` buttons side by side, use `destructive` for anything recoverable, or recolour a button with an agent or status colour.

## API

```ts
declare const buttonVariants: (props?: {
    variant?: "default" | "destructive" | "ghost" | "link" | "outline" | "secondary";
    size?: "default" | "icon" | "icon-lg" | "icon-sm" | "lg" | "sm";
} & ClassProp) => string;
declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants> & {
    asChild?: boolean;
}): any;
export { Button, buttonVariants };
```

## Example

```html
<div id="root" class="p-6"></div>
<script>
(function () {
  var h = React.createElement, DC = window.DigitalCrew, I = DC.Icons;
function Row(label, kids) {
  return h("div", { className: "flex flex-col gap-2" },
    h("span", { className: "text-xs text-muted-foreground" }, label),
    h("div", { className: "flex flex-wrap items-center gap-3" }, kids));
}
function App() {
  return h("div", { className: "flex flex-col gap-5" },
    Row("Variants", [
      h(DC.Button, { key: 1 }, h(I.Sparkles), "Write my campaign"),
      h(DC.Button, { key: 2, variant: "secondary" }, "Save draft"),
      h(DC.Button, { key: 3, variant: "outline" }, "Cancel"),
      h(DC.Button, { key: 4, variant: "ghost" }, "Skip"),
      h(DC.Button, { key: 5, variant: "destructive" }, "Delete campaign"),
      h(DC.Button, { key: 6, variant: "link" }, "View report")
    ]),
    Row("Sizes and icons", [
      h(DC.Button, { key: 1, size: "sm", variant: "outline" }, h(I.Plus), "New list"),
      h(DC.Button, { key: 2 }, "Continue", h(I.ArrowRight)),
      h(DC.Button, { key: 3, size: "lg" }, "Launch campaign"),
      h(DC.Button, { key: 4, size: "icon", variant: "outline", "aria-label": "Add prospect" }, h(I.Plus)),
      h(DC.Button, { key: 5, size: "icon-sm", variant: "ghost", "aria-label": "More actions" }, h(I.MoreHorizontal)),
      h(DC.Button, { key: 6, disabled: true }, h(I.Loader2, { className: "animate-spin" }), "Writing…")
    ]),
    Row("Glass finish (Appearance setting)", [
      h(DC.Button, { key: 1, "data-crew-button-finish": "glass", className: "relative isolate" }, "Review drafts"),
      h(DC.Button, { key: 2, "data-crew-button-finish": "glass", className: "relative isolate", size: "lg" }, "Get started")
    ])
  );
}
  ReactDOM.createRoot(document.getElementById("root")).render(h(DC.TooltipProvider, { delayDuration: 80 }, h(App)));
})();
</script>
```

## More in Actions

- [Badge](/design/components/badge.md): A small pill label: count, channel, state or tag (Max's `components/ui/badge.tsx`).
- [GlassButton](/design/components/glass-button.md): The Crew OS button with a frosted-glass finish: the same variants, sizes and `asChild` as `Button`, plus a backdrop blur and a soft highlight layer.
- [Toggle](/design/components/toggle.md): A two-state button (Radix toggle) that stays pressed until clicked again.
- [ToggleGroup](/design/components/toggle-group.md): A segmented set of toggles (Radix toggle group) where one or several options are on at a time.
