<!-- Generated by tools/docs.php from docs/src/btn.html - edit the source, not this file. -->

# Buttons

A button runs an action; a link drawn as a button goes somewhere. One class, `btn`, then a tone, a size or an icon when the button needs one.

## Examples

The plain `btn` is neutral. `btn-primary` marks the one main action of a screen; the other tones say what an action does.

```html
<button class="btn btn-primary" type="button">Save</button>
<button class="btn" type="button">Cancel</button>
<button class="btn btn-secondary" type="button">Duplicate</button>
<button class="btn btn-success" type="button">Publish</button>
<button class="btn btn-danger" type="button">Delete</button>
<button class="btn btn-warning" type="button">Withdraw</button>
<button class="btn btn-info" type="button">Preview</button>
```

### Outline, subtle, text and link

Quieter forms of the same tones for secondary actions, rows and toolbars. `btn-text` has no fill until hovered; `btn-link` reads as a link and keeps a button's size, and it alone wraps a long text.

```html
<button class="btn btn-outline-primary" type="button">Outline</button>
<button class="btn btn-outline-danger" type="button">Outline danger</button>
<button class="btn btn-subtle-primary" type="button">Subtle</button>
<button class="btn btn-subtle-danger" type="button">Subtle danger</button>
<button class="btn btn-text" type="button">Text</button>
<button class="btn btn-link" type="button">Link</button>
<button class="btn btn-link text-start" type="button">A long link wraps inside its box, like the title of a card that opens a window</button>
```

### Sizes

A size is a word: `btn-xs` 24 px, `btn-sm` 28 px, the default 32 px, `btn-lg` 40 px - the heights of every control of the same size. `btn-xl`, 56 px, is for a touch screen and the one action of a page; in a [kiosk](kiosk.md) it grows to a kiosk's size.

```html
<button class="btn btn-primary btn-xs" type="button">Extra small</button>
<button class="btn btn-primary btn-sm" type="button">Small</button>
<button class="btn btn-primary" type="button">Default</button>
<button class="btn btn-primary btn-lg" type="button">Large</button>
<button class="btn btn-primary btn-xl" type="button">Extra large</button>
```

### Icons

An icon sits beside the words, or alone in a square `btn-icon` whose name is its `aria-label`. `btn-close` draws the close glyph by itself.

```html
<button class="btn" type="button"><i class="bi bi-plus-lg" aria-hidden="true"></i>New excursion</button>
<a class="btn" href="#btn-icons">Open the site<i class="bi bi-box-arrow-up-right" aria-hidden="true"></i></a>
<button class="btn btn-icon" type="button" aria-label="Edit"><i class="bi bi-pencil" aria-hidden="true"></i></button>
<button class="btn btn-icon btn-text" type="button" aria-label="More actions"><i class="bi bi-three-dots" aria-hidden="true"></i></button>
<button class="btn btn-icon btn-sm btn-subtle-danger" type="button" aria-label="Remove"><i class="bi bi-trash3" aria-hidden="true"></i></button>
<button class="btn-close" type="button" aria-label="Close"></button>
```

### Groups and toolbars

`btn-group` joins buttons into one control - here a choice of view, each button pressed or not. `btn-toolbar` lays groups and buttons out in a wrapping row.

```html
<div class="btn-toolbar" role="group" aria-label="List view">
	<div class="btn-group" role="group" aria-label="Layout">
		<button class="btn" type="button" aria-pressed="true">List</button>
		<button class="btn" type="button" aria-pressed="false">Board</button>
		<button class="btn" type="button" aria-pressed="false">Calendar</button>
	</div>
	<div class="btn-group" role="group" aria-label="Export">
		<button class="btn" type="button">Export</button>
		<button class="btn btn-icon" type="button" aria-label="Export options"><i class="bi bi-chevron-down" aria-hidden="true"></i></button>
	</div>
</div>
```

### A choice of one

A segmented choice is Bootstrap's: in a `btn-group`, each `input type="radio" class="btn-check"` before its `label class="btn"`. The radio stays the control - the arrow keys move the choice, the form sends it - and the checked one is drawn pressed, an outline tone filled with its colour; the focus ring goes on the label. A link or a button that is the current one of a group carries `aria-current` and is drawn pressed too.

```html
<div class="btn-toolbar">
	<div class="btn-group" role="radiogroup" aria-label="Period">
		<input class="btn-check" type="radio" name="period" id="btn-segmented-day" value="day">
		<label class="btn btn-outline-primary" for="btn-segmented-day">Day</label>
		<input class="btn-check" type="radio" name="period" id="btn-segmented-week" value="week" checked>
		<label class="btn btn-outline-primary" for="btn-segmented-week">Week</label>
		<input class="btn-check" type="radio" name="period" id="btn-segmented-month" value="month">
		<label class="btn btn-outline-primary" for="btn-segmented-month">Month</label>
	</div>
	<div class="btn-group" role="radiogroup" aria-label="Status">
		<input class="btn-check" type="radio" name="state" id="btn-segmented-all" value="all" checked>
		<label class="btn" for="btn-segmented-all">All</label>
		<input class="btn-check" type="radio" name="state" id="btn-segmented-open" value="open">
		<label class="btn" for="btn-segmented-open">Open</label>
		<input class="btn-check" type="radio" name="state" id="btn-segmented-closed" value="closed">
		<label class="btn" for="btn-segmented-closed">Closed</label>
	</div>
	<nav class="btn-group" aria-label="Year">
		<a class="btn btn-sm" href="#btn-segmented">2025</a>
		<a class="btn btn-sm" href="#btn-segmented" aria-current="page">2026</a>
	</nav>
</div>
```

`btn-group-vertical` stacks the buttons of a group, each as wide as the widest.

```html
<div class="btn-group-vertical" role="group" aria-label="Order">
	<button class="btn" type="button"><i class="bi bi-arrow-up" aria-hidden="true"></i>Move up</button>
	<button class="btn" type="button"><i class="bi bi-arrow-down" aria-hidden="true"></i>Move down</button>
	<button class="btn" type="button"><i class="bi bi-trash" aria-hidden="true"></i>Remove</button>
</div>
```

### Toggle and busy

A toggle carries `aria-pressed`. While its action runs, `REDZ.ui.busy(button, promise)` shows a spinner in the button and marks it `aria-disabled`: it keeps the focus and ignores clicks until the promise settles. See [Busy](busy.md) for the words a button says while it waits.

```html
<button class="btn" type="button" aria-pressed="false" data-docs-run="toggle"><i class="bi bi-pin-angle" aria-hidden="true"></i>Pin</button>
<button class="btn btn-primary" type="button" data-docs-run="busy-button">Save for 2 seconds</button>
```

### A wait before it works again

`data-cooldown="N"` makes a button wait N seconds: it is `aria-disabled` - it keeps the focus and ignores clicks - and shows the seconds left after its words, in the page's text `cooldown`; then it wakes and the attribute goes. Setting the attribute again starts the wait again; a page reloaded during a wait renders the seconds left. The count is drawn, not read out every second: say the wait in words once, when it starts - "A new code is on its way; ask again in a minute". A cooldown set while the button is [busy](busy.md#busy-button) keeps it disabled after the wait, until its count ends.

```html
<button class="btn" type="button" data-cooldown="20" data-docs-run="cooldown"><i class="bi bi-arrow-clockwise" aria-hidden="true"></i>Send the code again</button>
```

## States

A state is the attribute accessibility needs anyway, on the button itself: no `active` or `disabled` classes. `aria-expanded` is set by the browser on a button that opens a menu or a dialog.

## Keyboard and accessibility

- `<button type="button">` for an action, `<a href>` for a place; `btn` draws both the same. Enter and Space press a button, Enter follows a link.
- A button with only an icon takes its name from `aria-label`; the icon is `aria-hidden="true"`.
- `disabled` takes a button out of the tab order and hides why. `aria-disabled="true"` keeps it focusable - so a tooltip can say why - and the library swallows its clicks.
- A button that opens a menu or a dialog uses the browser's command: `commandfor` + `command`, never `data-bs-toggle`.
- A segmented choice is radios: one Tab stop, the arrow keys move the choice, a screen reader says "1 of 3". A group of toggles that each stand alone is buttons with `aria-pressed`.
- The focus ring is 2 px of `--redz-focus`, 3:1 against the page in both themes.

## Tokens

From [`css/components/btn.css`](https://github.com/redz-tech/REDZ.UI/blob/main/css/components/btn.css). It declares `--redz-btn-bg`, `--redz-btn-fg`, `--redz-btn-hover`, `--redz-btn-hover-fg`, `--redz-btn-on-bg`, `--redz-btn-on-fg` on its root, never on rows or items.

It reads the theme's `--redz-bg-hover`, `--redz-text`, `--redz-control-height`, `--redz-space-12`, `--redz-radius-8`, `--redz-focus`, `--redz-bg-page`, `--redz-text-faint`, `--redz-primary`, `--redz-primary-contrast`, `--redz-secondary`, `--redz-secondary-contrast`, `--redz-success`, `--redz-success-contrast`, `--redz-danger`, `--redz-danger-contrast`, `--redz-warning`, `--redz-warning-contrast`, `--redz-info`, `--redz-info-contrast`, `--redz-primary-subtle`, `--redz-secondary-subtle`, `--redz-success-subtle`, `--redz-danger-subtle`, `--redz-warning-subtle`, `--redz-info-subtle`, `--redz-control-height-xs`, `--redz-radius-6`, `--redz-fs-12`, `--redz-control-height-sm`, `--redz-space-8`, `--redz-control-height-lg`, `--redz-space-16`, `--redz-fs-16`, `--redz-space-24`, `--redz-radius-12`, `--redz-fs-20`, `--redz-control-height-float`, `--redz-text-muted`, `--redz-motion`.

## From Bootstrap 5.3

| Bootstrap 5.3 | REDZ.UI | Change | Note |
| --- | --- | --- | --- |
| `btn-close-white` | - | remove | the close glyph takes the text color |
| `btn-dark` | - | review | no light or dark buttons: btn or btn-text |
| `btn-light` | - | review | no light or dark buttons: btn or btn-text |
| `btn-outline-dark` | - | review | btn-outline-secondary |
| `btn-outline-light` | - | review | btn-outline-secondary |
| `data-bs-toggle="button"` | `aria-pressed` | review | switch aria-pressed in your click handler |
