# Button

A labelled action in four emphases and three sizes; on the web an address makes it a link that looks the same.

The one filled button on a screen is its primary action; secondary actions are outlined and quiet ones are ghost. A destructive action is the Danger variant, placed last and never a red fill: both faces outline it in the bad tone. A busy button says what it is doing ("Saving…") and takes no clicks; on the web a button with an href is written as a link so it works without script, and the native face ignores href.

## Use it

Web:

```rust
use ocean_ui_core::{Button, Size, Variant};
Button::new("Save", Variant::Solid).size(Size::Md)
```

Native:

```rust
use ocean_ui_native::{Show, core::{Button, Size, Variant}};
Button::new("Deploy", Variant::Solid).size(Size::Md).show(ui);
```

Cargo feature `base`; since 2026-10-29.
In App UI the `button` block draws it ([button](/ui/blocks/button.md)).

## Props

| Name | Type | Default | What |
|---|---|---|---|
| `label` | `String` | `—` | The words on the button, a verb in sentence case. |
| `variant` | `Variant` | `—` | Solid, Outline, Ghost or Danger: how much the button stands out. |
| `size` | `Size` | `Md` | Sm, Md or Lg: 32, 36 or 44 px on the web and 28, 36 or 44 pt native; Lg is the public call to action. |
| `icon` | `Option<Icon>` | `None` | An icon before the label. |
| `busy` | `bool` | `false` | Working on it; the web face shows the busy label ("Working…" unless busy_label says otherwise) with aria-busy and takes no clicks; the native face shows a spinner. |
| `disabled` | `bool` | `false` | Takes no clicks and is dimmed. |
| `href` | `Option<String>` | `None` | An address; the web face writes the button as a link and the native face ignores it. |

On the Mac: ![Button, dark](/ui/native/button-dark.png) ![Button, light](/ui/native/button-light.png)

## Examples

### Variants

One solid button per screen; outline for the rest, ghost when quiet, danger last.

```rust
Button::new("Save", Variant::Solid).html(out);
Button::new("Preview", Variant::Outline).html(out);
Button::new("Cancel", Variant::Ghost).html(out);
Button::new("Delete", Variant::Danger).html(out);
```

### Sizes

32 px in dense tools, 36 px by default, 44 px for a public call to action.

```rust
Button::new("Small", Variant::Outline).size(Size::Sm).html(out);
Button::new("Medium", Variant::Outline).html(out);
Button::new("Large", Variant::Solid).size(Size::Lg).html(out);
```

### With an icon

The icon sits before the label; the label still names the action.

```rust
Button::new("New ticket", Variant::Solid).icon(Icon::Plus).html(out);
Button::new("Export", Variant::Outline).icon(Icon::Download).html(out);
```

### States

Busy says what it is doing and takes no clicks; disabled fades to 40 percent.

```rust
Button::new("Saving", Variant::Solid).busy(true).html(out);
Button::new("Publish", Variant::Outline).disabled(true).html(out);
```

### As a link

With an address the button is a link, so it works without script.

```rust
Button::new("Browse components", Variant::Outline).href("/ui/components")
```

## Accessibility

- The label is the accessible name, so an icon-only button needs words in its label.
- A busy or disabled button says so with aria-busy and disabled, or aria-disabled when it is a link.
- Focus shows a 2 px ring in the theme's ring colour; under a coarse pointer the target is at least 44 px.

## Related

- [Copy button](/ui/components/copy.md)
- [Menu](/ui/components/menu.md)
- [Dialog](/ui/components/dialog.md)
