## button (`poetry_button`)

Triggers an action or event, such as submitting a form or opening a dialog.

Class: Poetry::Ui::Button::Component - BEM block `poetry-ui-button`.
REQUIRED - one of a content block / with_leading / with_trailing / loading: (nothing visible renders without one - label: is only the accessible name); a call satisfying none raises.
- `size:` (symbol) - one of default|xs|sm|lg|icon|icon-xs|icon-sm|icon-lg, default "default", required - The size axis; the icon* sizes are square icon-only forms (label: required).
- `variant:` (symbol) - one of default|destructive|outline|secondary|ghost|link, default "default", required - The visual intent axis; :destructive marks irreversible actions.
- `disabled:` (boolean) - default false - Disables the control (native disabled; aria-disabled on the anchor form).
- `href:` (string) - The link target; implies the anchor form.
- `label:` (string) - The accessible name for icon-only usage - not visible text.
- `loading:` (boolean) - default false - The no-JS loading state: aria-busy, a spinner, and the control disabled.
- `tag:` (symbol) - one of button|a, default "button" - Renders the same styling on an <a> when :a - navigation wearing button clothes.
- `type:` (symbol) - one of button|submit|reset, default "button" - The native button type; ignored when the button renders as an anchor.
Slots: leading (Optional leading visual, rendered inside the icon span.), trailing (Optional trailing visual, rendered inside the icon span.).
- PART `button` - The rendered control itself (<button>, or <a> when tag: :a) - every visual state rides here | states: data-variant=default|destructive|outline|secondary|ghost|link (always - the resolved variant); data-size=default|xs|sm|lg|icon|icon-xs|icon-sm|icon-lg (always - the resolved size); data-loading (loading: is set (aria-busy rides along))
- PART `icon` - Wrapper span around leading/trailing slot content - sizes and centers whatever it holds
- PART `label` - The content block's span (display: contents - children join the root's flex row directly)
- PART `spinner` - The loading indicator, swapped in for the leading icon while loading:
In blocks: `action-bar`, `app-shell`, `data-index`, `destructive-panel`, `page-header`, `stepper`, `top-nav` - for a screen, start from the block (MCP compose/describe_block, or `bin/rails g poetry:block`), not from scratch.
- RULE: Use poetry_button - never a raw <button> with hand-written Tailwind.
- RULE: The visible text is the content block: poetry_button { "Save" }. label: is ONLY the accessible name.
- RULE: Icon-only buttons (size: :icon*) MUST pass label: (the accessible name).
- RULE: Link-styled actions use variant: :link - not <a> with button classes.
- RULE: Navigation wearing button styling: pass href: (renders a real <a>; tag: :a is implied) - never onclick navigation.
- RULE: Loading via loading: - never a manual disabled + spinner.
- RULE: Never nest an interactive element inside a Button.
- RULE: Pick the variant by intent; one primary (default) action per view.
