# Button

## Overview

The button component is a clickable control for actions such as submitting a form, confirming a choice, or triggering a UI change.

The component styles a native `<button>` element directly. It also provides the `.button` class for native links that navigate somewhere but need button-like visual prominence.

## When to use

Use a button to submit a form, confirm a choice, or trigger an immediate action on the current page.

## When not to use

Do not use a `<button>` to navigate to another destination or present a row of destination choices. Use a native link, optionally with the `.button` class for a prominent call to action, or use nav for a set of destination choices.

## Usage

```html
<button type="button">Primary Button</button>
```

For a standalone navigational call to action outside prose, apply `.button` to an anchor with a valid `href`:

```html
<a class="button" href="/applications/123">View application details</a>
```

The class changes only the visual treatment. The anchor remains a link: browsers and assistive technology identify it as a link, Enter activates it, and link features such as opening in a new tab remain available. Do not add `role="button"`, a Space-key handler, or JavaScript that imitates button behavior. Use destination-oriented link text that communicates the purpose when read out of context. Keep inline prose links unstyled, and let links owned by navigation, breadcrumbs, headings, headers, and footers use those components' link treatments rather than `.button`.

```html
<button type="button" style="--button-border-radius: 999px;">Rounded Button</button>
```

For a secondary action that should stay visually distinct from the primary fill, use the `secondary` helper:

```html
<button class="secondary" type="button">Secondary Button</button>
```

For a lower-emphasis action that should read as lighter-weight while keeping button affordances, use the `tertiary` helper:

```html
<button class="tertiary" type="button">Tertiary Button</button>
```

For a destructive action that should stand out from brand-colored actions, use the `error` helper:

```html
<button class="error" type="button">Delete Item</button>
```

Buttons marked with `aria-active="true"` use the primary filled treatment:

```html
<button aria-active="true" type="button">Active View</button>
```

When a button includes an icon, use the Material Symbols icon font through the shared `.icon` element pattern:

```html
<button type="button">
  <span class="icon" aria-hidden="true">add</span>
  <span>Create Item</span>
</button>
```

For icon-only buttons, apply the `.icon` class directly to the button and provide an accessible name:

```html
<button class="icon" type="button" aria-label="Add item">add</button>
```

For icon-only navigation, combine `.button` and `.icon` on a native link and provide an accessible name:

```html
<a class="button icon" href="/applications/123" aria-label="View application details">arrow_forward</a>
```

For buttons with a loading indicator, use `aria-busy="true"` and keep an action-specific visible label. Add `disabled` when the action must not be triggered again while it is processing:

```html
<button disabled aria-busy="true" type="button">Saving…</button>
```

Button loaders render at `75%` of `--button-icon-size`. Typical SVG icons include internal padding within their view box, while the CSS loading ring fills its entire box; the reduction gives both a comparable optical size. Keeping the loader derived from the icon token also preserves consistent button dimensions between busy buttons and the icon button variant when the shared icon size changes.

When space is constrained, an empty busy button centers the loading indicator. Provide its action-specific accessible name with `aria-label`:

```html
<button disabled aria-busy="true" type="button" aria-label="Saving report"></button>
```

Use this component when the user needs to trigger an immediate action.

Use a link when the interaction navigates to another page or location. Add `.button` to standalone links outside prose; keep inline and component-owned navigation links in their normal link treatment.

Use a navigation pattern instead when the interface needs a row of destination choices such as section links or a primary page bar, even if the visual treatment looks similar to grouped controls.

Button text should describe the action clearly and work without relying on surrounding context.

The button should use a native `<button>` element so it keeps keyboard behavior, disabled state support, and expected form interactions.

## Behavior

Covered by [Playwright behavior tests](../../tests/e2e/forms-inputs.spec.js).

- BTN-001: The button should size from its content, padding, and border radius rather than a fixed width.
- BTN-002: Buttons should reserve a `0.125rem` border in every variant so switching visual treatments does not change the outer size.
- BTN-003: Buttons should use vertical padding that resolves with the reserved border to `0.75rem` of occupied space per side.
- BTN-004: Buttons can override the shared control radius with `--button-border-radius` when an action needs a different corner treatment.
- BTN-005: Enabled buttons should show the pointer cursor to reinforce that the control is clickable, while disabled buttons should not.
- BTN-006: Primary buttons should keep the primary filled treatment on hover and move to the strong primary tone when pressed.
- BTN-007: Button text should either match the button's visible surface or stroke color, or use the inverse text color when the button is filled.
- BTN-008: Secondary buttons should keep a visible accent-text stroke with a matching label until pressed, then use the same strong pressed fill and inverse label as the primary button.
- BTN-009: Tertiary buttons should stay borderless with accent text by default, show a subtle primary stroke on hover, and keep the accent label on the subtle primary pressed fill.
- BTN-010: Button color changes should transition smoothly, with reduced-motion preferences disabling the transition.
- BTN-011: Focus-visible should use each variant's hover treatment and add a visible ring.
- BTN-012: Error buttons should use the error color scale so destructive actions remain visually distinct from primary actions.
- BTN-013: Buttons with `aria-active="true"` should use the primary filled treatment so the active choice reads with stronger emphasis.
- BTN-014: Disabled primary and tertiary buttons should use the disabled accent fill, while disabled secondary buttons should keep a visible neutral stroke.
- BTN-015: Buttons with icons should keep the icon inline with the label and adjust spacing without requiring a separate variant class.
- BTN-016: Busy buttons should keep disabled visual styling when disabled, use the wait cursor, and space the generated loading indicator consistently with an inline icon.
- BTN-017: Icon-only buttons should use the same vertical padding as text buttons, remain square, center the glyph, and keep an accessible name through `aria-label` when no visible text is present.
- BTN-018: Buttons should not be used as stand-ins for destination navigation just because the interface groups them horizontally.
- BTN-019: Empty busy buttons should remain square, center the generated loading indicator, and require an accessible name such as `aria-label`.
- BTN-020: An anchor with `.button` should receive the button visual treatment while retaining native link semantics and Enter-only keyboard activation.
- BTN-021: An icon-only anchor with `.button.icon` should remain square and retain the foreground color of its button variant.
## Accessibility

- Use a native `<button>` for actions so keyboard and assistive technology behavior stays intact.
- Use `<a class="button" href="…">` for standalone navigation outside prose. Keep a valid `href`, omit `role="button"`, and do not add button keyboard behavior; the class changes appearance, not semantics.
- Give icon-only links an accessible name with `aria-label`.
- Make link text describe its destination or purpose clearly without relying on surrounding context.
- Provide an explicit `type` when the button appears in a form so submit behavior is intentional.
- Keep the visible label descriptive enough to stand on its own.
- Mark decorative icons with `aria-hidden="true"` so they do not add noise to the accessible name.
- Use `aria-busy="true"` only while an action is processing, and remove it when the action finishes.
- Pair `aria-busy="true"` with `disabled` when repeated activation must be prevented; `aria-busy` does not disable the button by itself.
- Do not rely on color alone to communicate disabled or pressed state.

## Variables

| Variable | Type | Description |
| --- | --- | --- |
| `--button-action-padding-block` | `<length>` | Sets --button-action-padding-block so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--button-action-padding-inline` | `<length>` | Sets --button-action-padding-inline so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--button-active-background` | `<color>` | Sets the pressed or active fill color; change it when active buttons need a stronger, weaker, or variant-specific state surface. |
| `--button-active-border-color` | `<color>` | Sets the pressed or active border color; change it with the active background when the button outline should track state changes. |
| `--button-active-color` | `<color>` | Sets the pressed or active label and icon color; change it when the active surface needs a different contrast color. |
| `--button-background` | `<color>` | Sets the resting fill color for filled button variants; change it to create primary, danger, or contextual button treatments. |
| `--button-border-color` | `<color>` | Sets the resting border color; change it for outlined variants or when the button edge needs different emphasis from the fill. |
| `--button-border-radius` | `<length>` | Sets the button corner radius and grouped-control radius; change it to make buttons more rounded, squared, or search-pill shaped. |
| `--button-border-width` | `<length>` | Sets the button stroke width and contributes to computed height; change it when buttons need a heavier or lighter outline. |
| `--button-color` | `<color>` | Sets the resting label and icon color; change it when the button background or border treatment requires a different contrast color. |
| `--button-color-focus-ring` | `<color>` | Sets --button-color-focus-ring so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--button-control-gap` | `<length>` | Sets --button-control-gap so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--button-control-radius` | `<length>` | Sets --button-control-radius so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--button-disabled-background` | `<color>` | Sets the disabled fill color; change it when unavailable actions should appear more subtle or keep a transparent surface. |
| `--button-disabled-border-color` | `<color>` | Sets the disabled border color; change it when disabled outlined buttons need a visible but noninteractive edge. |
| `--button-disabled-color` | `<color>` | Sets the disabled label and icon color; change it to keep disabled text legible while still reading inactive. |
| `--button-focus-ring-size` | `<length>` | Sets --button-focus-ring-size so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--button-font-family-sans` | `Any` | Sets --button-font-family-sans so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--button-font-size` | `<length>` | Sets --button-font-size so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--button-height` | `<length>` | Sets the minimum button height after padding, line-height, and border space are combined; change it to tune control density. |
| `--button-hover-background` | `<color>` | Sets the hover fill color; change it when pointer hover should preview a stronger or subtler interaction state. |
| `--button-hover-border-color` | `<color>` | Sets the hover border color; change it when outlined or filled buttons need a distinct pointer affordance. |
| `--button-hover-color` | `<color>` | Sets the hover label and icon color; change it when the hover surface needs a different contrast pairing. |
| `--button-hover-scale-inline` | `<number>` | Sets the horizontal scale applied on hover; change it to adjust or remove the slight expansion interaction. |
| `--button-icon-fill` | `<number>` | Sets --button-icon-fill so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--button-icon-font-family` | `Any` | Sets --button-icon-font-family so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--button-icon-grad` | `<number>` | Sets --button-icon-grad so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--button-icon-opsz` | `<number>` | Sets --button-icon-opsz so the component can default to shared theme or layout tokens while exposing an overrideable component API. |
| `--button-icon-size` | `<length>` | Sets the font size for icons and busy indicators in buttons; change it when icon glyphs should differ from the control text scale. |
| `--button-icon-wght` | `<number>` | Sets --button-icon-wght so the component can default to shared theme or layout tokens while exposing an overrideable component API. |

## Depends On

- `--color-accent-disabled`
- `--color-border`
- `--color-error`
- `--color-focus-ring`
- `--color-primary`
- `--color-primary-strong`
- `--color-primary-subtle`
- `--color-text-accent`
- `--color-text-inverse`
- `--color-text-subtle`
- `--font-family-sans`
- `--font-line-height-body`
- `--font-size`
- `--icon-fill`
- `--icon-font-family`
- `--icon-grad`
- `--icon-opsz`
- `--icon-wght`
- `--layout-action-padding-block`
- `--layout-action-padding-inline`
- `--layout-control-border-width`
- `--layout-control-gap`
- `--layout-control-icon-size`
- `--layout-control-min-height`
- `--layout-control-radius`
- `--layout-focus-ring-size`
- [Loading](./loading.md)
