# Atoms — Button

A pill button built from Gazelle's **Tailwind v4 utilities** (the theme in `tailwind.css`) — no
component class required. Compose three things: the shared base list, exactly one **style**, and
(for every style except `link`) exactly one **size**. The copy field on the Atoms → Button screen
emits this exact markup. A framework-free `.gz-btn` API in `components/button.css` mirrors the
same design for projects not on Tailwind.

## Metadata

- **Category:** Atoms
- **Status:** Stable
- **Source:** `tailwind.css` (theme utilities) + `docs/atoms.js` (canonical markup);
  `components/button.css` (`.gz-btn` framework-free compatibility API)

## When to use

- Build the class list from three parts: the **base** utilities (below), one **style**, and —
  for every style except `link` — one **size**. `link` is size-agnostic; never add a size to it.
- Wrap the visible text in a `<span>`; add an optional chevron `<svg>` before or after it —
  leading vs. trailing is decided by DOM order, not a class.
- Size the icon to match the button: `size-5` at `lg`, `size-4` at `sm`/`xs` and always for
  `link`. Always add `shrink-0`.

## When not to use

- In a Tailwind project, don't reach for the `.gz-btn` compatibility classes — apply the
  utilities so the copied class list stays the single source of truth. Use `.gz-*` only where
  Tailwind isn't available.
- Never hand-write a hex or px value. Every utility resolves to a Gazelle theme token; if one is
  missing, add it to the theme (`tailwind.css`) rather than an arbitrary literal.
- Do not use the `tertiary` style on a light or plain surface. Its `bg-white/10`, `backdrop-blur`
  and oyster border/label are tuned for dark or photographic backgrounds only.

## Anatomy

```html
<button type="button" class="<base> <style> <size>">
  <span>Button</span>
  <svg class="size-5 shrink-0" viewBox="0 0 24 24" fill="none" aria-hidden="true"
       stroke-width="1.5"><path d="M9 6L15 12L9 18" stroke="currentColor"
       stroke-linecap="round" stroke-linejoin="round" /></svg>
</button>
```

**Base** (every button):

```
inline-flex cursor-pointer appearance-none items-center justify-center whitespace-nowrap
border border-solid font-brand font-medium leading-100 uppercase tracking-wide no-underline
transition-[background-color,border-color,color,box-shadow] duration-[120ms] focus:outline-none
```

## Styles

Each style sets shape, fill, border and label, plus `hover:` / `focus-visible:` variants. Every
style shares the same focus ring (see States).

| Style | Default utilities | Hover |
| --- | --- | --- |
| `primary` | `rounded-full border-surface-brand bg-surface-brand text-fg-inverse` | `hover:border-brand-blue-gazelle-600 hover:bg-brand-blue-gazelle-600` |
| `white` | `rounded-full border-white bg-white text-fg-brand` | `hover:border-brand-blue-gazelle-600 hover:bg-brand-blue-gazelle-600 hover:text-fg-inverse` |
| `secondary` | `rounded-full border-surface-oyster bg-surface-oyster text-fg-brand` | `hover:border-oyster-800 hover:bg-oyster-800` |
| `tertiary` | `rounded-full border-oyster-500 bg-white/10 text-oyster-500 backdrop-blur-[5px]` | `hover:border-oyster-800 hover:bg-white/20` |
| `link` | `h-auto gap-[2px] rounded-sm border-transparent bg-transparent px-0 py-[2px] text-14 text-fg-brand` | `hover:text-brand-blue-gazelle-600` |

## Sizes

Applied to every style except `link` (which sets its own height, padding and type above).

| Size | Utilities | Icon |
| --- | --- | --- |
| `lg` (48px) | `h-12 gap-1 px-5 text-14` | `size-5 shrink-0` |
| `sm` (40px) | `h-10 gap-[2px] px-4 text-12` | `size-4 shrink-0` |
| `xs` (32px) | `h-8 gap-[2px] px-3 text-12` | `size-4 shrink-0` |

## States

Default → `hover:` → `focus-visible:`. The focus ring is the same for every style, drawn with
Tailwind's ring utilities (a `surface-page`-coloured gap, then a `focus-ring` ring) rather than a
plain outline:

```
focus-visible:ring-1 focus-visible:ring-focus-ring
focus-visible:ring-offset-2 focus-visible:ring-offset-surface-page
```

The Atoms screen renders a Default / Hover / Focus matrix; its Hover and Focus columns are static
previews of the `hover:` / `focus-visible:` variants above.

## Tokens & utilities

- **Colour** — through theme utilities only: `bg-surface-brand`, `text-fg-inverse`,
  `border-surface-oyster`, `bg-brand-blue-gazelle-600`, `ring-focus-ring`, etc. Never a raw hex.
- **Shape** — `rounded-full` for every style except `link` (`rounded-sm`).
- **Type** — `font-brand font-medium uppercase tracking-wide` plus `text-14` / `text-12`. There
  is deliberately no shared `--type-button-*` composite or `.gz-text-*` role — the button owns
  its own two-step type scale, so match these utilities rather than inventing a text role.

## Common mistake

Pairing `link` with a size, or using `tertiary` outside a dark/photographic surface — both
silently produce the wrong result rather than an error. In a Tailwind project, another trap is
copying the `.gz-btn` compatibility classes instead of the utilities; keep the utility list as
the single source of truth.

## Cross-references

- `specs/foundations/color.md` — the roles these fills/borders/labels resolve to.
- `specs/foundations/radius.md` — why every button is `rounded-full`.
- `specs/icons/icons.md` — sizing the optional chevron `<svg>`.
- `specs/atoms/choice.md` — the other shipped atom family, checkbox & radio.
