# Icon Button

An icon-only button for compact, labeled actions like adding, editing, or closing.

An `aria-label` is required: icon-only buttons have no visible text, so the accessible name comes from it.

## Installation

```tsx
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
```
## Usage

```tsx
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { AddOutline } from "@makeplane/propel/icons";

export default function BasicDemo() {
  return (
    <IconButton
      variant="primary"
      size="sm"
      aria-label="Add item"
      icon={<Icon icon={AddOutline} />}
    />
  );
}
```
## Examples

### Tooltip

`IconButton` shows its `aria-label` in a tooltip on hover and keyboard focus by default. Set
`showTooltip={false}` to hide it. For a different label, position, shortcut, or delay, wrap the button
in `Tooltip` and set `showTooltip={false}` so only one popup appears.

### Variants

Variant sets the visual weight and palette: `primary`, `secondary`, `tertiary`, and `ghost`, plus
`danger` (solid fill) and `danger-outline` (bordered outline) for destructive actions.

```tsx
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { AddOutline, DeleteOutline } from "@makeplane/propel/icons";

export default function VariantsDemo() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <IconButton
        variant="primary"
        size="sm"
        aria-label="Add member"
        icon={<Icon icon={AddOutline} />}
      />
      <IconButton
        variant="secondary"
        size="sm"
        aria-label="Add member"
        icon={<Icon icon={AddOutline} />}
      />
      <IconButton
        variant="tertiary"
        size="sm"
        aria-label="Add member"
        icon={<Icon icon={AddOutline} />}
      />
      <IconButton
        variant="ghost"
        size="sm"
        aria-label="Add member"
        icon={<Icon icon={AddOutline} />}
      />
      <IconButton
        variant="danger"
        size="sm"
        aria-label="Delete project"
        icon={<Icon icon={DeleteOutline} />}
      />
      <IconButton
        variant="danger-outline"
        size="sm"
        aria-label="Delete project"
        icon={<Icon icon={DeleteOutline} />}
      />
    </div>
  );
}
```

### Sizes

`size` sets a square box — `xs` 20 / `sm` 24 / `md` 28 / `lg` 32 px. The glyph runs one step
larger than a labelled control at the same rung (14 / 16 / 16 / 20 px), because there is no
label to balance it. `xs` is under WCAG 2.5.8's 24px floor and ships a transparent ring that
brings the pointer target to 24×24 without moving a painted pixel — which is also why it is
unavailable inside contiguous groups, where adjacent rings would intersect.

```tsx
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { AddOutline } from "@makeplane/propel/icons";

export default function SizesDemo() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <IconButton
        variant="secondary"
        size="xs"
        aria-label="Add label"
        icon={<Icon icon={AddOutline} />}
      />
      <IconButton
        variant="secondary"
        size="sm"
        aria-label="Add label"
        icon={<Icon icon={AddOutline} />}
      />
      <IconButton
        variant="secondary"
        size="md"
        aria-label="Add label"
        icon={<Icon icon={AddOutline} />}
      />
      <IconButton
        variant="secondary"
        size="lg"
        aria-label="Add label"
        icon={<Icon icon={AddOutline} />}
      />
    </div>
  );
}
```

### Loading

The loading state shows a spinner, sets `aria-busy`, and blocks interaction.

```tsx
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { RefreshOutline } from "@makeplane/propel/icons";

export default function LoadingDemo() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <IconButton
        variant="primary"
        size="sm"
        aria-label="Syncing workspace"
        loading
        icon={<Icon icon={RefreshOutline} />}
      />
      <IconButton
        variant="secondary"
        size="sm"
        aria-label="Syncing workspace"
        loading
        icon={<Icon icon={RefreshOutline} />}
      />
      <IconButton
        variant="tertiary"
        size="sm"
        aria-label="Syncing workspace"
        loading
        icon={<Icon icon={RefreshOutline} />}
      />
    </div>
  );
}
```

### Disabled

`disabled` is the hard, non-focusable disabled state; unlike `loading`, it removes the button from the tab order.

```tsx
import { Icon } from "@makeplane/propel/components/icon";
import { IconButton } from "@makeplane/propel/components/icon-button";
import { AddOutline, DeleteOutline } from "@makeplane/propel/icons";

export default function DisabledDemo() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <IconButton
        variant="primary"
        size="sm"
        aria-label="Add item"
        disabled
        icon={<Icon icon={AddOutline} />}
      />
      <IconButton
        variant="danger-outline"
        size="sm"
        aria-label="Delete item"
        disabled
        icon={<Icon icon={DeleteOutline} />}
      />
    </div>
  );
}
```
## API Reference

### IconButton

The ready-made icon-only button: grafts Base UI's `Button` behavior onto the square `IconButton`
box, filling it with the provided icon element and swapping in a spinner while `loading`. It can
render as `<a>` via `nativeButton={false}` + `render={<a href=… />}` when needed. An `aria-label`
is REQUIRED for the accessible name and appears in a tooltip by default.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` (required) | `"primary" \| "secondary" \| "tertiary" \| "ghost" \| "danger" \| "danger-outline"` | — |  |
| `size` (required) | `"xs" \| "sm" \| "md" \| "lg"` | — |  |
| `icon` (required) | `ReactNode` | — | The single icon element to render, usually `<Icon icon={...} />`. The accessible name comes from `aria-label`. |
| `disabled` | `boolean` | — | Hard, non-focusable native disabled state (`loading` stays focusable instead). |
| `type` | `"button" \| "submit" \| "reset"` | `button` | The button's form behavior. |
| `aria-label` | `string` | — | Defines a string value that labels the current element. Required: icon-only buttons have no visible text, so they must be labeled. @see aria-labelledby. |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |
| `nativeButton` | `boolean` | — | Set `false` when `render` swaps the underlying tag away from `<button>` (e.g. an `<a>`): Base UI then adds `role`, tab focus, and Enter/Space activation as appropriate. |
| `showTooltip` | `boolean` | `true` | Show the accessible label in a tooltip on hover (after `Tooltip`'s 300ms) or focus (immediately). Set false when a caller supplies its own tooltip, including one with different timing. |
| `loading` | `boolean` | `false` | Shows a spinner in place of the icon, sets `aria-busy`, and makes the button non-interactive. |
