# Image action button
> Generated from image-action-button.contract.json — do not edit by hand.

The compact per-option image control — an image icon button with empty / loading / thumbnail states and a Change/Edit/Delete menu; it emits intents, the host owns the modals.

Tier: **leaf-lit**. Frameworks: HTML (paste-and-run, no build step) · React · Vue 3.

Surfaces: editor, settings.

## Props
| Prop | Type | Default | Notes |
| --- | --- | --- | --- |
| `state` | empty | loading | filled | `empty` | The control's state — drives which affordance shows |
| `src` | string | `—` | The thumbnail image URL when filled |
| `label` | string | `Add image` | The empty-state tooltip / aria-label |
| `disabled` | boolean | `false` | Non-interactive; grey fill |
| `add / change / edit / delete` | CustomEvent | `—` | Composed intents — the host runs the corresponding modal/flow |

## Visual standard (measured)
- Trigger: 36×36 icon button, radius 8, 1px #E3E3E3 border
- Empty: system-image-square glyph + "Add image" tooltip; click emits `add`
- Loading: spinner at the same 36×36 size — no layout jump; role=status, aria-label "Loading", aria-busy
- Filled: thumbnail + hover pencil overlay; click opens Change/Edit/Delete
- Menu: Change / Edit / Delete (Delete in #F5222D); each emits an intent
- Intents: add · change · edit · delete — host owns the modals, this owns none
- Disabled: #F1F1F1 fill, #EBEBEB border, not-allowed

## When to use
- **Image action button** — a per-OPTION image cell in an option row
- **Image dropzone** — a standalone MAIN image field where the image IS the field (SETTINGS-44)

Per-option image support is all-or-nothing: present with its FULL state set, or the control is omitted entirely — never a disabled/greyed stub when unsupported (SETTINGS-22). It is a compact icon button, not a text "Image" button (SETTINGS-32). It emits intents; the host owns the upload/crop modals — never a self-built uploader.
