# Icon Button

> Square icon-only button with a mandatory accessible label and an automatic tooltip (with optional shortcut).

- **Slug:** `icon-button` · **Category:** primitives · **Status:** stable
- **Import:** `import { IconButton } from "@kura-ui/react/icon-button";`
- **Docs:** https://kuro.abstergo.fyi/components/icon-button
- **Registry item:** https://kuro.abstergo.fyi/r/icon-button.json
- **Tags:** button, icon, toolbar, action, tooltip

## Install

```bash
# shadcn registry (copies source into your project)
npx shadcn@latest add https://kuro.abstergo.fyi/r/icon-button.json
# Kura CLI (same, plus dependency resolution and --json output)
npx @kura-ui/cli add icon-button
# npm package
npm install @kura-ui/react
```

## When to use

- Toolbar and composer actions: attach, mic, send, stop, copy, retry.
- Any action represented only by an icon.

## When not to use

- Actions that need a visible text label — use button.
- Toggles with on/off state in a group — compose button with aria-pressed.

## Props

| Prop | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `label` | `string` | — | yes | Accessible name and tooltip text. |
| `icon` | `React.ReactNode` | — | yes | The icon element, e.g. <ArrowUp /> from lucide-react. |
| `tooltip` | `boolean` | `true` | no | Show label (and shortcut) in a tooltip. |
| `shortcut` | `readonly KeyName[]` | — | no | Shortcut shown in the tooltip (display only). |
| `tooltipSide` | `"top" \| "right" \| "bottom" \| "left"` | `"top"` | no | Tooltip side. |
| `variant` | `"primary" \| "secondary" \| "ghost" \| "outline" \| "accent" \| "danger"` | `"ghost"` | no | Same variants as button; defaults to ghost. |
| `size` | `"xs" \| "sm" \| "md" \| "lg"` | `"md"` | no | 24 / 28 / 32 / 40px square. |

Native attributes of the root element, `className` and `ref` are passed through.

## Accessibility

- `label` becomes aria-label, so the control always has an accessible name.
- The tooltip opens on focus, so keyboard users see the label and shortcut too.
- Reduced motion: Inherits button: no press scale; tooltip fades without travel.

| Keys | Action |
| --- | --- |
| Enter / Space | Activate |

## Notes for agents

- Never pass aria-label; use `label`. The shortcut is display-only — bind it with useHotkey or kbd-shortcut.

## Examples

### Basic (minimal)

```tsx
import { Copy } from "lucide-react";
import { IconButton } from "@kura-ui/react/icon-button";

export function IconButtonMinimal() {
  return <IconButton label="Copy" icon={<Copy />} />;
}
```

### Composer toolbar (full)

```tsx
import { ArrowUp, Mic, Paperclip, RotateCcw } from "lucide-react";
import { IconButton } from "@kura-ui/react/icon-button";

export function IconButtonFull() {
  return (
    <div className="flex items-center gap-1 rounded-kura-xl bg-kura-surface p-1.5 shadow-kura-sm">
      <IconButton label="Attach file" icon={<Paperclip />} size="sm" />
      <IconButton label="Dictate" icon={<Mic />} size="sm" shortcut={["mod", "d"]} />
      <IconButton label="Retry" icon={<RotateCcw />} size="sm" tooltipSide="bottom" />
      <span className="mx-1 h-4 w-px bg-kura-border" />
      <IconButton
        label="Send"
        icon={<ArrowUp />}
        size="sm"
        variant="primary"
        shortcut={["mod", "enter"]}
        className="rounded-full"
      />
      <IconButton
        label="Sending disabled"
        icon={<ArrowUp />}
        size="sm"
        variant="secondary"
        disabled
        tooltip={false}
      />
    </div>
  );
}
```

### Message action bar (composition)

Copy with a confirm swap, pressed feedback toggles, a loading regenerate and shortcut tooltips under an assistant reply.

```tsx
"use client";

import { Check, Copy, RotateCcw, Share, ThumbsDown, ThumbsUp, Volume2 } from "lucide-react";
import * as React from "react";
import { IconButton } from "@kura-ui/react/icon-button";

export function IconButtonMessageActions() {
  const [copied, setCopied] = React.useState(false);
  const [rating, setRating] = React.useState<"up" | "down" | null>(null);
  const [regenerating, setRegenerating] = React.useState(false);
  const timers = React.useRef<ReturnType<typeof setTimeout>[]>([]);
  React.useEffect(() => () => timers.current.forEach(clearTimeout), []);

  const copy = () => {
    setCopied(true);
    timers.current.push(setTimeout(() => setCopied(false), 1600));
  };
  const regenerate = () => {
    setRegenerating(true);
    timers.current.push(setTimeout(() => setRegenerating(false), 1400));
  };

  return (
    <article
      aria-label="Assistant reply"
      className="flex w-full max-w-lg flex-col gap-3 rounded-kura-xl bg-kura-surface p-4 shadow-kura-sm"
    >
      <p className="text-[15px] text-kura-fg leading-relaxed">
        The dip lines up with the 2.4 release: onboarding completion fell from 71% to 58% the day
        the new permissions step shipped. Moving that step after the first project is created should
        recover most of it.
      </p>
      <div className="flex items-center justify-between gap-3">
        <div
          role="toolbar"
          aria-label="Message actions"
          className="-ml-1.5 flex items-center gap-0.5"
        >
          <IconButton
            label={copied ? "Copied" : "Copy"}
            icon={copied ? <Check className="text-kura-success-text" /> : <Copy />}
            size="sm"
            shortcut={["mod", "shift", "c"]}
            onClick={copy}
          />
          <IconButton
            label="Good response"
            icon={<ThumbsUp />}
            size="sm"
            aria-pressed={rating === "up"}
            className="aria-pressed:bg-kura-active aria-pressed:text-kura-fg"
            onClick={() => setRating((r) => (r === "up" ? null : "up"))}
          />
          <IconButton
            label="Bad response"
            icon={<ThumbsDown />}
            size="sm"
            aria-pressed={rating === "down"}
            className="aria-pressed:bg-kura-active aria-pressed:text-kura-fg"
            onClick={() => setRating((r) => (r === "down" ? null : "down"))}
          />
          <IconButton label="Read aloud" icon={<Volume2 />} size="sm" />
          <span aria-hidden className="mx-1 h-4 w-px bg-kura-border" />
          <IconButton
            label="Regenerate"
            icon={<RotateCcw />}
            size="sm"
            shortcut={["mod", "r"]}
            loading={regenerating}
            onClick={regenerate}
          />
          <IconButton label="Share" icon={<Share />} size="sm" />
        </div>
        <p className="shrink-0 font-kura-mono text-kura-fg-subtle text-xs tabular-nums">
          Sonnet 5.5 · 2.1s
        </p>
      </div>
    </article>
  );
}
```

## Dependencies

- npm: none
- Kura registry items: `kura-theme`, `button`, `kura-keys`, `tooltip`, `utils`
- Related: `button`, `tooltip`, `kbd-shortcut`
