# Button

> The themed button: variants, sizes, icons and shortcuts. A native Pyzed component for Python desktop GUIs, with a live preview, code examples and its API.

Source: https://albakore.github.io/pyzed/library/button/

:::tip[In plain words]
A button is something you click to make the app do something: save, send, open, delete.
:::

Displays a themed button. It reads its colors from the current theme, can carry an icon and a keyboard shortcut, and comes in several variants and sizes. Any style parameter of `pz.button` (`bg`, `hover_bg`, `color`, `shadow`, `radius`…) overrides the theme's.

`ui.button` · Category: [Actions](https://albakore.github.io/pyzed/library/#actions) · [Full reference →](https://albakore.github.io/pyzed/reference/ui/#button)

**Code:**

```python
pz.row([
    ui.button("Cancel", on_cancel, variant="ghost"),
    ui.button("Save", on_save, icon="save", variant="solid", keys="ctrl-s"),
], spacing=8)
```

## When to use

- For the main actions of a form, dialog or toolbar.
- Use `variant="solid"` for the one primary action and `"ghost"` for secondary ones.
- For a button with only an icon, use [Icon button](https://albakore.github.io/pyzed/library/icon-button/).

## Usage

```python
import pyzed as pz
from pyzed import ui

class Editor(pz.App):
    def save(self):
        ...

    def view(self):
        return ui.button("Save", self.save, icon="save", keys="ctrl-s")
```

## Variants

:::note[About these captures]
Each capture is recorded by pyzed itself, off-screen at 2x, in One Dark or One Light to match this site's theme, and cropped to the component so it shows at its real size. The pointer hovers, presses or types into it; a component that does not react to the pointer cycles through a few themes. With reduced motion you see the first frame. Open **Code** under a capture to see the snippet that draws it; free names such as `on_save` stand for your own callbacks.
:::

### Variant

`filled` is the default; use `solid` for the one main action of a screen.

```python
ui.button("Filled", on_press)
```

```python
ui.button("Ghost", on_press, variant="ghost")
```

```python
ui.button("Subtle", on_press, variant="subtle")
```

```python
ui.button("Outline", on_press, variant="outline")
```

```python
ui.button("Solid", on_press, variant="solid")
```

```python
ui.button("Tinted", on_press, variant="tinted")
```

### Semantic colors

A color name as `variant` gives a tinted button; `tint=` colors a `solid` one.

```python
ui.button("Upgrade", on_press, variant="accent", icon="sparkles")
```

```python
ui.button("Done", on_press, variant="success", icon="check")
```

```python
ui.button("Careful", on_press, variant="warning", icon="triangle-alert")
```

```python
ui.button("Delete", on_press, variant="error", icon="trash-2")
```

```python
ui.button("Details", on_press, variant="info", icon="info")
```

```python
ui.button("Delete forever", on_press, variant="solid", tint="error")
```

### Size

```python
ui.button("Extra small", on_press, size="xs")
```

```python
ui.button("Small", on_press, size="sm")
```

```python
ui.button("Medium", on_press, size="md")
```

```python
ui.button("Large", on_press, size="lg")
```

### Content

```python
ui.button("Save", on_press, icon="save")
```

```python
ui.button("Next", on_press, icon="arrow-right", icon_position="end")
```

```python
ui.button("Save", on_press, icon="save", keys="ctrl-s")
```

```python
ui.button("Online", on_press, icon="circle", icon_color="success")
```

```python
ui.button(icon="plus", on_press=on_press)
```

### State

A button without `on_press` is drawn disabled, so unfinished actions look unfinished.

```python
ui.button("Selected", on_press, selected=True)
```

```python
ui.button("Disabled", on_press, disabled=True)
```

```python
ui.button("Not wired yet")
```

```python
pz.container(ui.button("Sign in", on_press, variant="solid", full_width=True), width=280)
```

### Style overrides

The style arguments of `pz.button` (`bg`, `hover_bg`, `pressed_bg`, `color`, `border_color`, `shadow`, `radius`, `padding`) replace the theme's values for that button only.

```python
ui.button("Lifted", on_press, variant="solid", shadow=("#00000066", (0, 4), 12))
```

```python
ui.button("Custom", on_press, bg="#c678dd", hover_bg="#d08ae6", color="#ffffff")
```

```python
ui.button("Gold border", on_press, variant="outline", border_color="#e5c07b")
```

```python
ui.button("Pill", on_press, variant="solid", radius=999)
```

### States and events

```python
ui.button("Saving", on_press, icon="save", loading=True)
```

```python
ui.button("Save", on_press, tooltip="Writes the file")
```

### Base widget

The `pz.*` widget underneath: no theme, every color and size is yours.

```python
pz.button("pz.button", on_press, padding=(6, 14), bg="#3a7bc8", color="#ffffff", radius=6)
```

```python
pz.button(pz.row([pz.icon("heart", size=14), pz.text("Like")], spacing=6, align="center"), on_press,
          padding=(6, 12), bg="#c678dd22", color="#c678dd", border_color="#c678dd", border_width=1, radius=999)
```

## Examples

### Full width

`full_width=True` stretches the button to its container.

**Code:**

```python
pz.column([
    ui.button("Sign in", on_press, variant="solid", full_width=True),
    ui.button("Create an account", on_press, variant="outline", full_width=True),
], spacing=8, width=320)
```

## Events

Button has **1 event of its own**, listed below. Like every widget it also has the **18 common events** (`click`, `double_click`, `right_click`, `mouse_enter`, `drag`, `key_down`, `resize`…), attached with `.on(...)`: see [Events](https://albakore.github.io/pyzed/guides/events/) and [Events of every component](https://albakore.github.io/pyzed/library/events/).

| Event | Of | Handler gets | When |
| --- | --- | --- | --- |
| `on_press` | `ui.button`, `pz.button` | `()` | Message or callable sent on press. `None` disables the control. |

**Handler gets** is what Pyzed passes to your callable: `()` means nothing. Any handler can also be a message (a value that is not callable), which goes to `update()`.

### Handling the events

The same events written three ways. **Imperative**: methods of the app change `self`. **Declarative**: the handlers produce messages and one `update()` decides. **Functional**: a pure function draws an immutable state and asks for changes with `set_state(...)`. See [Imperative, declarative and functional](https://albakore.github.io/pyzed/guides/code-styles/).

**Imperative:**

```python
class Editor(pz.App):
    def __init__(self):
        self.saved = 0

    def save(self):                      # on_press: called with nothing
        self.saved += 1

    def view(self):
        return pz.row([
            ui.button("Save", self.save, icon="save", variant="solid", keys="ctrl-s"),
            ui.button("Reset", lambda: setattr(self, "saved", 0), variant="ghost"),
            ui.label(f"Saved {self.saved} times"),
        ], spacing=8, align="center")
```

**Declarative:**

```python
class Editor(pz.App):
    saved = 0

    def update(self, message):           # every button sends a message here
        match message:
            case "save":
                self.saved += 1
            case "reset":
                self.saved = 0

    def view(self):
        return pz.row([
            ui.button("Save", "save", icon="save", variant="solid", keys="ctrl-s"),
            ui.button("Reset", "reset", variant="ghost"),
            ui.label(f"Saved {self.saved} times"),
        ], spacing=8, align="center")
```

**Functional:**

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class State:
    saved: int = 0

def view(state, set_state):              # a pure function: state in, widgets out
    return pz.row([
        ui.button("Save", lambda: set_state(saved=state.saved + 1), icon="save", variant="solid"),
        ui.button("Reset", lambda: set_state(saved=0), variant="ghost"),
        ui.label(f"Saved {state.saved} times"),
    ], spacing=8, align="center")
```

## Tips

:::tip
To pass a value to the callback, bind it with a default argument: `ui.button(name, lambda n=name: self.open(n))`.
:::

## Properties and API

### `ui.button`

```python
button(
    content=None,
    on_press=None,
    *,
    icon=None,
    icon_position='start',
    icon_color=None,
    variant='filled',
    tint=None,
    size='md',
    selected=False,
    disabled=False,
    full_width=False,
    keys=None,
    loading=False,
    tooltip=None,
    theme=None,
    **style,
)
```

A themed button with variants, sizes, an icon and a shortcut.

| Property | Default | Description |
| --- | --- | --- |
| `content` | `None` | Text or widget. |
| `icon` | `None` | Name of a Lucide icon (`pyzed icons` lists them). |
| `icon_position` | `"start"` | `"start"` or `"end"`. |
| `icon_color` | `None` | Icon color: a theme token or a hex color. |
| `variant` | `"filled"` | `"filled"`, `"ghost"`, `"subtle"`, `"outline"`, `"solid"`, `"tinted"`, or a semantic color: `"accent"`, `"error"`, `"warning"`, `"success"`, `"info"`. |
| `tint` | `None` | Color for the `solid` and `tinted` variants. |
| `size` | `"md"` | `"xs"`, `"sm"`, `"md"` or `"lg"`. |
| `selected` | `False` | Marks it as selected. |
| `disabled` | `False` | Disables it (ignores clicks). |
| `full_width` | `False` | Fills the full width. |
| `keys` | `None` | Shortcut shown at the end, written as a string: `"ctrl-s"`, `"cmd-k cmd-s"`. |
| `loading` | `False` | Shows a spinner in place of the icon and ignores presses (keep a `pz.every(1/30, ...)` subscription while it spins). |
| `tooltip` | `None` | Text shown on hover. |
| `theme` | `None` | Theme to use instead of the current one (`pyzed.theme.current()`). |
| `**style` | — | Any `pyzed.button` style parameter (`bg`, `hover_bg`, `color`…). |

Events: `on_press` ([events](#events)).

[Full reference →](https://albakore.github.io/pyzed/reference/ui/#button)

### `pz.button`

```python
button(
    content,
    on_press=None,
    *,
    padding=None,
    width=None,
    height=None,
    bg=None,
    color=None,
    border_color=None,
    border_width=0.0,
    radius=None,
    shadow=None,
    hover_bg=None,
    hover_color=None,
    hover_border_color=None,
    pressed_bg=None,
    disabled_color=None,
    clip=False,
)
```

A button with no predefined style. For the themed button use `ui.button`.

| Property | Default | Description |
| --- | --- | --- |
| `content` | *required* | Button content. |
| `padding` | `None` | Inner padding: `8`, `(vertical, horizontal)` or `(top, right, bottom, left)`. |
| `width` | `None` | Width: pixels, `"fill"`, `"shrink"` or `"fill:N"` (proportion). |
| `height` | `None` | Height: pixels, `"fill"`, `"shrink"` or `"fill:N"` (proportion). |
| `bg` | `None` | Background color (`"#rrggbb[aa]"`, `(r, g, b[, a])` or `"transparent"`). |
| `color` | `None` | Text or icon color. |
| `border_color` | `None` | Border color. |
| `border_width` | `0.0` | Border width in pixels. |
| `radius` | `None` | Corner radius: a number or `(top_left, top_right, bottom_right, bottom_left)`. |
| `shadow` | `None` | Shadow: `(color, (dx, dy), blur)`. |
| `hover_bg` | `None` | Background on hover. |
| `hover_color` | `None` | Text color on hover. |
| `hover_border_color` | `None` | Border color on hover. |
| `pressed_bg` | `None` | Background while pressed. |
| `disabled_color` | `None` | Text color when disabled. |
| `clip` | `False` | Clips content that overflows the bounds. |

Events: `on_press` ([events](#events)).

[Full reference →](https://albakore.github.io/pyzed/reference/widgets/#button)

## Related

- [Icon button](https://albakore.github.io/pyzed/library/icon-button/): A square button with just an icon and an optional tooltip.
- [Split button](https://albakore.github.io/pyzed/library/split-button/): A main action plus an arrow that opens more options.
- [Toggle group](https://albakore.github.io/pyzed/library/toggle-group/): An icon-only segmented control.
- [Tooltip](https://albakore.github.io/pyzed/library/tooltip/): A hint shown on hover, optionally with its shortcut.
