<!-- Generated by scripts/build-agent-kit.ts for @aistrike-dev/ui@5.0.1. Do not edit. -->

# Button vs IconButton vs FAB vs SpeedDial

<!-- use-when: A control that performs an action: labelled, icon-only, or floating. -->

Four ways to render a control that does something. They differ in **whether the control has a visible
text label** and **whether it sits in the content flow or floats above it**.

| Control | Label | Position | How many per screen |
| --- | --- | --- | --- |
| `Button` | Visible text | In the content flow | As many as the design needs |
| `IconButton` | Icon only, `label` for assistive tech | In the content flow | Many — toolbars, table rows |
| `FAB` | Icon, floating | Floats above content | Exactly one |
| `SpeedDial` | Icon, floating, expands to actions | Floats above content | Exactly one |
| `ButtonGroup` | Wraps `Button`s | In the content flow | Groups related actions |

The rule of thumb: **if there is room for a text label, use one.** An icon alone is only unambiguous for
a small set of universally understood actions (close, edit, delete, more). `IconButton` therefore
*requires* a `label` prop — it is rendered as `aria-label`, and there is no way to omit it.

## Decision flow

```
Does the control float above the content, in a fixed corner?
        │
        ├── Yes
        │     ├── One action ──────────────────► FAB
        │     └── A cluster of related actions ──► SpeedDial
        │
        └── No — it sits in the content flow
                │
                ├── Is it icon-only, for space or because
                │   the icon is unambiguous? ─────► IconButton (with `label`)
                │
                └── It has a text label
                        │
                        ├── Several related actions forming
                        │   one visual unit ─────► ButtonGroup of Buttons
                        └── Otherwise ──────────► Button

Selecting rather than acting? None of these - see selection-controls.
```

## Button

The default for **any action with a text label**. Variants form an emphasis ladder: `primary` (at most
one per view), `secondary` (the default), `ghost` (tertiary), `destructive`.

```tsx
import { Button } from '@aistrike-dev/ui';
import { Stack } from '@mui/material';

<Stack direction="row" spacing={1}>
  <Button variant="ghost" onClick={cancel}>Cancel</Button>
  <Button variant="primary" onClick={save}>Save changes</Button>
</Stack>
```

**Practical limits:** the label names the action ("Save changes", "Send invite"), never "OK" or "Click
here", and never a full sentence. Pick `size` from context — `small` in dense toolbars and tables,
`medium` in forms, `large` for a prominent CTA. Show a loading state for anything slow. `startIcon` and
`endIcon` decorate a labelled button; if you find yourself with an icon and no label, that is an
`IconButton`. Buttons are for actions, not navigation between pages — that is a link. The full variant
rules live in the `Button` component reference.

## IconButton

An icon-only action with an **enforced accessible label**, plus an optional tooltip, semantic variants
(`default`, `primary`, `danger`) and a `loading` state.

**Reach for it when:**

- Table row actions where a text label would not fit.
- Toolbar controls: refresh, expand, copy, download.
- Close buttons on dialogs, drawers and alerts.
- A "more" overflow that opens a `Menu`.

```tsx
import { IconButton } from '@aistrike-dev/ui';
import DeleteIcon from '@mui/icons-material/Delete';

<IconButton
  icon={<DeleteIcon />}
  label="Delete finding"
  tooltip="Delete finding"
  variant="danger"
  size="small"
  onClick={remove}
/>
```

**Practical limits:** `icon` and `label` are both required. The `label` becomes `aria-label`, so write
what the action does ("Delete finding"), not what the icon is ("trash"). Add `tooltip` whenever the icon
is not universally understood — disabled icon buttons stay tooltip-hoverable via a span wrapper, so the
user can still find out why. `loading` sets `aria-busy` and blocks interaction. Use `variant="danger"`
for destructive row actions rather than styling the icon by hand.

## FAB

A single floating action for **the one thing users do most on this screen**. One per screen, no
exceptions.

**Reach for it when:**

- A dense list or table whose main action ("New scan", "Add asset") would otherwise scroll away.
- Narrow viewports, where a header action bar is cramped.

```tsx
import { FAB } from '@aistrike-dev/ui';
import AddIcon from '@mui/icons-material/Add';

<FAB color="primary" aria-label="Add asset" onClick={create}>
  <AddIcon />
</FAB>
```

**Practical limits:** always pass `aria-label` for the icon-only form — unlike `IconButton`, the FAB does
not force it, so it is the easiest place in the system to ship an unlabelled control. `variant="extended"`
adds a text label and is the better choice whenever the icon alone is ambiguous. Two FABs on one screen
means neither is the primary action; put the rest in the header.

## SpeedDial

A floating button that **expands to a small cluster of related actions**. It is a FAB whose single action
is "show me the others".

**Reach for it when:**

- A dense screen where three or four related actions all need to be reachable.
- Export as CSV / JSON / PDF from a floating position.

```tsx
import { SpeedDial, SpeedDialAction } from '@aistrike-dev/ui';
import ShareIcon from '@mui/icons-material/Share';

<SpeedDial ariaLabel="Export options" direction="up">
  <SpeedDialAction icon={<ShareIcon />} tooltipTitle="Export as CSV" onClick={exportCsv} />
</SpeedDial>
```

**Practical limits:** `ariaLabel` is required, and every action needs a `tooltipTitle` — without it the
expanded actions are unidentifiable icons. Keep it to a handful of actions; beyond that a `Menu` from an
`IconButton` scales better and is more familiar. For a single action, use a `FAB`. Never primary
navigation.

## ButtonGroup

Wraps several `Button`s into one visual unit. It groups **actions**, and holds no selected state — if one
segment should stay visibly active after clicking, you wanted `ToggleButtonGroup`.

```tsx
import { ButtonGroup, Button } from '@aistrike-dev/ui';

<ButtonGroup>
  <Button onClick={exportCsv}>CSV</Button>
  <Button onClick={exportJson}>JSON</Button>
</ButtonGroup>
```

## Worked examples

| Scenario | Control | Why |
| --- | --- | --- |
| Save / Cancel on a form | `Button` ×2, one `primary` | Labelled actions with an emphasis order |
| "Delete 12 findings" | `Button variant="destructive"` + confirmation | Irreversible, needs a label |
| Delete on a table row | `IconButton variant="danger"` | No room for a label |
| Close a dialog | `IconButton` labelled "Close" | Universally understood icon |
| Row overflow "..." opening a menu | `IconButton` labelled "More actions" | Standard overflow pattern |
| Refresh a dashboard widget | `IconButton` with a tooltip | Compact, in a widget header |
| "New scan" on a dense findings screen | `FAB` (or `variant="extended"`) | The one dominant action |
| Export as CSV / JSON / PDF, floating | `SpeedDial` | A cluster of related floating actions |
| Export as CSV / JSON / PDF, in a toolbar | `ButtonGroup`, or a `Menu` | In-flow related actions |
| Table view vs card view | Not a button — see selection-controls | A selection, not an action |
| Enable a setting | `Switch` | A toggle, not an action |
| Go to the asset detail page | A link, not a `Button` | Navigation |

## Anti-patterns

- **A raw `<button>` or a custom-styled clickable `div`.** Every action control is a design-system
  `Button` or `IconButton`.
- **An icon-only `Button`.** Use `IconButton`, which enforces the accessible label.
- **An `IconButton` whose `label` names the glyph.** "trash" tells a screen-reader user nothing; "Delete
  finding" does.
- **A `FAB` with no `aria-label`.** The FAB does not enforce it, so this is the easiest unlabelled
  control to ship by accident.
- **More than one `FAB` or `SpeedDial` per screen.** Then nothing is the primary action.
- **More than one `primary` `Button` in a view.** It dilutes the hierarchy; promote exactly one.
- **`destructive` without a confirmation step** for anything irreversible.
- **A `Button` used to navigate between pages.** Use a link, so middle-click and open-in-new-tab work.
- **A `ButtonGroup` for mutually exclusive selection.** It has no selected value; use
  `ToggleButtonGroup`.
- **A sentence inside a `Button`.** Put the explanation next to it, not in the label.
- **A `SpeedDial` with unlabelled actions.** Every action needs `tooltipTitle`.

## Accessibility

- `Button` renders a native `<button>`: keyboard operable, with a visible focus ring via
  `Mui-focusVisible`. Do not re-implement either.
- `IconButton` requires `label`, rendered as `aria-label`. `loading` sets `aria-busy` and disables
  interaction. Disabled buttons remain tooltip-hoverable via a span wrapper, so the user can learn why
  a control is unavailable.
- `FAB` needs `aria-label` supplied by you whenever it is icon-only.
- `SpeedDial` requires `ariaLabel`, and each action exposes its `tooltipTitle` to assistive tech.
- In a `ButtonGroup`, every button stays independently focusable.

## When you are unsure, ask

The ambiguous case is usually **which action is `primary`** — real designs often show several buttons of
equal weight — and occasionally **whether an action deserves a `FAB` at all**. **If you cannot
confidently choose, stop and ask, naming the options and the one you lean toward.**

> "This toolbar has Rescan, Export and Isolate. I'm making them all `secondary` with none promoted,
> since I can't tell which is the main action. Should one be `primary`?"

> "'New scan' is the obvious main action on this screen. I'd put it in the page header rather than a
> `FAB`, since the screen is desktop-first and the header stays visible. Do you want it floating
> instead?"

A single clarifying question is far cheaper than shipping a control whose behaviour surprises the user.

## References

- **Atoms → Button**, **Atoms → IconButton**, **Atoms → FAB**, **Organisms → SpeedDial**,
  **Molecules → ButtonGroup** — stories for each, including variants and loading states.
- **Guidelines → Choosing → Selection Controls** — for controls that hold a selection.
- [MUI Button](https://mui.com/material-ui/react-button/) · [MUI Floating Action Button](https://mui.com/material-ui/react-floating-action-button/) · [MUI Speed Dial](https://mui.com/material-ui/react-speed-dial/)
