> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Button - Web (AI Kit)

> Buttons are interactive controls that trigger an action or event.

> [!NOTE]
>
> For development, import `Button` from
> `@servicetitan/anvil2-ai-kit`. For design, find it in the
> [AI Kit Web Component Library file](https://www.figma.com/design/QEIsArLktLeJAaFvcpw3PO/AI-Kit-Web-Component-Library).

ai-kit-button

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-button?file=src/App.tsx)

```tsx lines theme={null}
import { Button } from "@servicetitan/anvil2-ai-kit";

function App() {
  return <Button label="Button" />;
}

export default App;
```

## Options

The Button supports variants, sizes, and icon configurations to fit different action hierarchies.

### Variants

ai-kit-button-variant

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-button-variant?file=src/App.tsx)

```tsx lines theme={null}
import { Button } from "@servicetitan/anvil2-ai-kit";

function App() {
  return (
    <div
      style={{
        display: "flex",
        flexWrap: "wrap",
        gap: 12,
        alignItems: "center",
      }}
    >
      <Button label="Primary" variant="primary" />
      <Button label="Secondary" variant="secondary" />
      <Button label="Ghost" variant="ghost" />
      <Button label="Danger" variant="danger" />
    </div>
  );
}

export default App;
```

| Variant | When to use |
| - | - |
| Primary | The main action in a view. Prefer one primary action in the same context |
| Secondary | Supporting actions that sit alongside or below a primary action |
| Ghost | Low-emphasis actions, including many icon-only controls |
| Danger | Destructive or irreversible actions that need clear caution |

### Sizes

ai-kit-button-size

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-button-size?file=src/App.tsx)

```tsx lines theme={null}
import { Button } from "@servicetitan/anvil2-ai-kit";

function App() {
  return (
    <div
      style={{
        display: "flex",
        flexWrap: "wrap",
        gap: 12,
        alignItems: "center",
      }}
    >
      <Button label="Extra Small" size="xsmall" variant="secondary" />
      <Button label="Small" size="small" variant="secondary" />
      <Button label="Medium" size="medium" variant="secondary" />
    </div>
  );
}

export default App;
```

| Size | Typical use |
| - | - |
| Medium | Default size for most product actions |
| Small | Dense layouts where space is limited |
| Xsmall | Compact desktop UI. On touch devices, the tap target is promoted to meet accessibility guidance |

### Icons

ai-kit-button-icon

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-button-icon?file=src/App.tsx)

```tsx lines theme={null}
import { Button } from "@servicetitan/anvil2-ai-kit";
import {
  IconChevronRight,
  IconPencil,
  IconPlus,
} from "@servicetitan/anvil2-icons";

function App() {
  return (
    <div
      style={{
        display: "flex",
        flexDirection: "column",
        gap: 12,
        alignItems: "center",
      }}
    >
      <Button
        label="Add item"
        variant="secondary"
        icon={{ icon: <IconPlus />, position: "left" }}
      />
      <Button
        label="Continue"
        variant="secondary"
        icon={{ icon: <IconChevronRight />, position: "right" }}
      />
      <Button label="Edit" variant="secondary" icon={<IconPencil />} />
    </div>
  );
}

export default App;
```

Icons can sit before or after the label, or replace the label entirely. Icon-only buttons keep a short label for accessibility and show that label in a tooltip on hover and focus.

## Behavior

The Button responds to pointer, touch, and keyboard interaction with clear resting, hover, pressed, focused, and disabled states.

### Visual States

ai-kit-button-visual-states

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-button-visual-states?file=src/App.tsx)

```tsx lines theme={null}
import { Button } from "@servicetitan/anvil2-ai-kit";

const variants = ["primary", "secondary", "ghost", "danger"] as const;

function App() {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 24 }}>
      {variants.map((variant) => (
        <div
          key={variant}
          style={{
            display: "flex",
            flexWrap: "wrap",
            gap: 12,
            alignItems: "center",
          }}
        >
          <Button label="Rest" variant={variant} size="small" />
          <Button
            label="Hover"
            variant={variant}
            size="small"
            data-interactive="hover"
          />
          <Button
            label="Active"
            variant={variant}
            size="small"
            data-interactive="active"
          />
          <Button
            label="Focus Visible"
            variant={variant}
            size="small"
            data-interactive="focus-visible"
          />
          <Button label="Disabled" variant={variant} size="small" isDisabled />
        </div>
      ))}
    </div>
  );
}

export default App;
```

Hover and press update the surface to confirm the control is interactive. Keyboard focus shows a visible focus ring. Disabled buttons remain in the tab order where appropriate but do not activate.

### Icon-only labeling

ai-kit-button-icon-only-labeling

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-button-icon-only-labeling?file=src/App.tsx)

```tsx lines theme={null}
import { Button } from "@servicetitan/anvil2-ai-kit";
import { IconPencil } from "@servicetitan/anvil2-icons";

function App() {
  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        padding: "48px 24px",
      }}
    >
      <Button
        label="Edit"
        variant="secondary"
        size="small"
        icon={<IconPencil />}
      />
    </div>
  );
}

export default App;
```

Icon-only buttons always carry a required label. That label becomes the accessible name and the tooltip content so pointer and assistive-technology users share the same affordance name.

## Usage Guidelines

### When to Use

Use the Button when:

* **Triggering an action** – A user needs to run a command or event, such as adding or editing an item.
* **Marking the main action** – One action leads the view. Use the primary variant, and prefer one primary action in the same context.
* **Confirming a destructive action** – The action deletes data or can't be undone. Use the danger variant so the caution is clear.
* **Compact icon controls** – Space is tight and an icon communicates the action. Use an icon-only Button with a short label, which also appears as its tooltip.

### When not to use

Avoid using the Button for:

* **Navigating to another page or location** – Use the [Link](https://anvil.servicetitan.com/docs/components/link/ai-kit.md) instead.
* **Holding a persistent on/off state** – Use the [Button Toggle](https://anvil.servicetitan.com/docs/components/button-toggle/ai-kit.md) instead.
* **Offering follow-up prompts after an agent response** – Use the [Suggestion](https://anvil.servicetitan.com/docs/components/suggestion/web.md) instead.

## Keyboard Interaction

Users can activate the Button using standard keyboard controls.

| Key | Description |
| - | - |
| Tab | Moves focus to the button |
| Enter / Space | Activates the focused button |

### Accessibility

Every Button requires a label so it always has an accessible name. Icon-only buttons expose that label through `aria-label` and a presentational tooltip so the name is not announced twice. Prefer press handlers that work for click, touch, and keyboard activation together.

## Code

**Implementation**

ai-kit-button-playground

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-button-playground?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Button } from "@servicetitan/anvil2-ai-kit";
import { IconPlus } from "@servicetitan/anvil2-icons";

function App() {
  return (
    <Button
      variant="primary"
      size="medium"
      label="Add item"
      icon={{ icon: <IconPlus />, position: "left" }}
      onPress={() => console.log("pressed")}
    />
  );
}

export default App;
```

## Common Examples

To use the `Button`, import it from `@servicetitan/anvil2-ai-kit` and pass a required `label`. The kit forbids `children`; all content flows through typed props.

```tsx theme={null}
import { Button } from "@servicetitan/anvil2-ai-kit";

function ExampleComponent() {
  return <Button label="Save" onPress={() => {}} />;
}
```

### Variants

The `variant` prop sets visual intent. Use `"primary"` for the main action, `"secondary"` for supporting actions, `"ghost"` for low-emphasis actions, and `"danger"` for destructive work.

ai-kit-button-variant

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-button-variant?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Button } from "@servicetitan/anvil2-ai-kit";

function App() {
  return (
    <div
      style={{
        display: "flex",
        flexWrap: "wrap",
        gap: 12,
        alignItems: "center",
      }}
    >
      <Button label="Primary" variant="primary" />
      <Button label="Secondary" variant="secondary" />
      <Button label="Ghost" variant="ghost" />
      <Button label="Danger" variant="danger" />
    </div>
  );
}

export default App;
```

### Sizes

The `size` prop sets the target size. `"medium"` is the default. On touch devices, `"xsmall"` promotes to a 40×40 minimum so the tap target meets WCAG 2.5.5 guidance.

ai-kit-button-size

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-button-size?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Button } from "@servicetitan/anvil2-ai-kit";

function App() {
  return (
    <div
      style={{
        display: "flex",
        flexWrap: "wrap",
        gap: 12,
        alignItems: "center",
      }}
    >
      <Button label="Extra Small" size="xsmall" variant="secondary" />
      <Button label="Small" size="small" variant="secondary" />
      <Button label="Medium" size="medium" variant="secondary" />
    </div>
  );
}

export default App;
```

### Icons

Pass a bare icon element for an icon-only button. Pair `{ icon, position }` with the visible `label` to place an icon before or after the text. Import icons from `@servicetitan/anvil2-icons`.

ai-kit-button-icon

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-button-icon?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Button } from "@servicetitan/anvil2-ai-kit";
import {
  IconChevronRight,
  IconPencil,
  IconPlus,
} from "@servicetitan/anvil2-icons";

function App() {
  return (
    <div
      style={{
        display: "flex",
        flexDirection: "column",
        gap: 12,
        alignItems: "center",
      }}
    >
      <Button
        label="Add item"
        variant="secondary"
        icon={{ icon: <IconPlus />, position: "left" }}
      />
      <Button
        label="Continue"
        variant="secondary"
        icon={{ icon: <IconChevronRight />, position: "right" }}
      />
      <Button label="Edit" variant="secondary" icon={<IconPencil />} />
    </div>
  );
}

export default App;
```

Icon-only buttons apply `label` as `aria-label` and show it in a presentational tooltip, so pointer and assistive-technology users share the same name.

ai-kit-button-icon-only-labeling

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-button-icon-only-labeling?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Button } from "@servicetitan/anvil2-ai-kit";
import { IconPencil } from "@servicetitan/anvil2-icons";

function App() {
  return (
    <div
      style={{
        display: "flex",
        justifyContent: "center",
        padding: "48px 24px",
      }}
    >
      <Button
        label="Edit"
        variant="secondary"
        size="small"
        icon={<IconPencil />}
      />
    </div>
  );
}

export default App;
```

### Disabled

Set `isDisabled` to prevent press and hover while keeping the control focusable for keyboard users.

ai-kit-button-disabled

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-button-disabled?file=src/App.tsx)

```tsx lines expandable theme={null}
import { Button } from "@servicetitan/anvil2-ai-kit";

function App() {
  return (
    <div
      style={{
        display: "flex",
        flexWrap: "wrap",
        gap: 12,
        alignItems: "center",
      }}
    >
      <Button label="Primary" variant="primary" isDisabled />
      <Button label="Secondary" variant="secondary" isDisabled />
      <Button label="Ghost" variant="ghost" isDisabled />
      <Button label="Danger" variant="danger" isDisabled />
    </div>
  );
}

export default App;
```

### Press handling

Prefer `onPress` over `onClick`. `onPress` fires for click, touch, and Space/Enter, matching React Aria's press semantics.

```tsx theme={null}
<Button label="Save" onPress={() => save()} />
```

## React Accessibility

* Every `Button` requires a `label`, which is always the accessible name.
* Icon-only buttons expose that name through `aria-label` and a presentational tooltip so screen readers do not announce the name twice.
* Disabled buttons remain in the tab order. They do not activate.
* Users activate a focused button with Enter or Space.

**Button Props**

```tsx theme={null}
<Button
  label="Save"
  variant="primary"
  size="medium"
  icon={{ icon: <IconPlus />, position: "left" }}
  isDisabled={false}
  onPress={() => {}}
/>
```

## `Button` Props

The `Button` accepts the following props. It also forwards remaining React Aria `Button` props except `children`, `className`, `style`, and `isPending`.

**label** `string` *required*

Required accessible name. Becomes visible text when the button shows a
label, and becomes the `aria-label` plus tooltip content for icon-only
buttons. Pass a short imperative such as `"Save"` or `"Open settings"`.

**className** `string`

Caller-supplied class name merged onto the button's recipe class.

**icon** `IconElement | \{ icon: IconElement; position: "left" | "right" \}`

Optional icon. A bare `IconElement` renders an icon-only button. The
object form pairs the icon with the visible `label`.

**isDisabled** `boolean` (default: false)

Disables press and hover. The control remains focusable for keyboard
users.

**onPress** `(e: PressEvent) =\> void`

Press handler that fires for click, touch, and Space/Enter. Prefer this
over `onClick` for keyboard and touch parity.

**size** `"medium" | "small" | "xsmall"` (default: medium)

Target size. On touch devices, `"xsmall"` is promoted to a 40×40 minimum.

**variant** `"primary" | "secondary" | "ghost" | "danger"` (default: primary)

Visual intent. Use `"primary"` for the main action and `"danger"` for
destructive work.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.