# Typography

Type sets for headings, body copy, labels, buttons, amounts, and code.

## Overview

Tempo sets its web interface in Geist, with Geist Mono for values and code. Each typography set bundles a family, size, weight, line height, and tracking, so you apply one token, such as `typography: 'copy.14'`, instead of five properties.

```tsx
import { style } from '@tempoxyz/ds'

export const fill = true
export function Demo() {
  return (
    <dl {...styles.list()}>
      <dt {...styles.token()}>heading.32</dt>
      <dd {...styles.heading32()}>Balance</dd>

      <dt {...styles.token()}>heading.24</dt>
      <dd {...styles.heading24()}>Send payment</dd>

      <dt {...styles.token()}>heading.20</dt>
      <dd {...styles.heading20()}>Recent activity</dd>

      <dt {...styles.token()}>heading.16</dt>
      <dd {...styles.heading16()}>API keys</dd>

      <dt {...styles.token()}>copy.16</dt>
      <dd {...styles.copy16()}>Payments settle in about one second.</dd>

      <dt {...styles.token()}>copy.14</dt>
      <dd {...styles.copy14()}>Webhooks retry failed deliveries for 24 hours.</dd>

      <dt {...styles.token()}>copy.13</dt>
      <dd {...styles.copy13()}>Updated 2 minutes ago</dd>

      <dt {...styles.token()}>label.14.strong</dt>
      <dd {...styles.label14()}>Amount</dd>

      <dt {...styles.token()}>label.12</dt>
      <dd {...styles.label12()}>Block time</dd>

      <dt {...styles.token()}>button.14</dt>
      <dd {...styles.button14()}>Add funds</dd>

      <dt {...styles.token()}>label.14.mono</dt>
      <dd {...styles.mono()}>0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc</dd>
    </dl>
  )
}

namespace styles {
  export const button14 = style({ margin: '0', typography: 'button.14' })

  export const copy13 = style({ color: 'gray.900', margin: '0', typography: 'copy.13' })

  export const copy14 = style({ margin: '0', typography: 'copy.14' })

  export const copy16 = style({ margin: '0', typography: 'copy.16' })

  export const heading16 = style({ margin: '0', typography: 'heading.16' })

  export const heading20 = style({ margin: '0', typography: 'heading.20' })

  export const heading24 = style({ margin: '0', typography: 'heading.24' })

  export const heading32 = style({ margin: '0', typography: 'heading.32' })

  export const label12 = style({ color: 'gray.900', margin: '0', typography: 'label.12' })

  export const label14 = style({ margin: '0', typography: 'label.14.strong' })

  export const list = style({
    alignItems: 'baseline',
    color: 'foreground',
    columnGap: '6',
    display: 'grid',
    gridTemplateColumns: '8rem minmax(0, 1fr) !custom',
    margin: '0',
    rowGap: '4',
  })

  export const mono = style({ margin: '0', overflowWrap: 'anywhere', typography: 'label.14.mono' })

  export const token = style({ color: 'gray.900', typography: 'label.12.mono' })
}
```

[View example source](https://github.com/tempoxyz/ds/blob/main/docs/src/examples/demos/Foundations/TypeScale.tsx)

## Typefaces

| Family     | Used for                                           | Source                                                                              |
| ---------- | -------------------------------------------------- | ----------------------------------------------------------------------------------- |
| Geist      | Every `heading`, `copy`, `label`, and `button` set | Bundled as one variable face covering weights 100 to 900, with `font-display: swap` |
| Geist Mono | The `.mono` sets and `fontFamily: 'mono'`          | Bundled as one variable face covering weights 100 to 900, with `font-display: swap` |

The Zyzz plugin emits both faces from the package's contracts. Each falls back to the system UI or monospace font while it loads. Fallback fonts have different widths, so a fallback rendering doesn't prove that a layout matches Figma.

## Type Sets

Sizes are in pixels. Web uses Zyzz's default sets unchanged. Headings track tighter as they grow, from -0.28px at `heading.14` to -4.32px at `heading.72`.

| Set         | Sizes                                         | Weight                                | Use for                                                       |
| ----------- | --------------------------------------------- | ------------------------------------- | ------------------------------------------------------------- |
| `heading.*` | 14, 16, 20, 24, 32, 40, 48, 56, 64, 72        | 600, or 500 with `.subtle` (16 to 32) | Page, dialog, and card titles, and large amounts              |
| `copy.*`    | 13, 14, 16, 18, 20, 24                        | 400, or 550 with `.strong` (14 to 24) | Paragraphs, descriptions, table cells, and messages           |
| `label.*`   | 12, 13, 14, 16, 18, 20                        | 400, or 500 with `.strong` (12 to 16) | Field labels, menu items, tags, and other single-line UI text |
| `button.*`  | 12, 14, 16                                    | 500                                   | Button labels only                                            |
| `.mono`     | `copy.13`, `label.12`, `label.13`, `label.14` | 400                                   | Addresses, hashes, keys, and code                             |

`copy` sets have taller line heights for wrapping text, such as 24px on `copy.16`. `label` sets keep lines tight, such as 20px on `label.16`, since labels rarely wrap.

### Where Components Use Each Set

| Context                                   | Set                                                                                    |
| ----------------------------------------- | -------------------------------------------------------------------------------------- |
| Page title in `AppShell.Title`            | `heading.24`                                                                           |
| `Dialog.Title`                            | `heading.24`, with a `copy.16` description                                             |
| `Surface.Title`, `Popover.Title`, legends | `heading.20`                                                                           |
| `Empty` title                             | `heading.16`                                                                           |
| Body text in `Surface`, `Table`, `Toast`  | `copy.14`                                                                              |
| Field labels                              | `label.12.strong` to `label.14.strong`, following the control `size`                   |
| Menu and select items                     | `label.14.strong`, or `label.13.strong` and `label.16.strong` by size                  |
| `Tag` and `Kbd`                           | `label.12` or `label.14`, by size                                                      |
| `Button`                                  | `button.12`, `button.14`, `button.16` for `small`, `medium`, `large`                   |
| `Amount`                                  | `copy.14.strong` (`sm`), `heading.20` (`md`), `heading.32` (`lg`), `heading.40` (`xl`) |

## Guidelines

### Build Hierarchy With Few Sizes

Most product screens need three steps: a `heading.24` page title, `heading.20` section or card titles, and `copy.14` body text. Add `copy.13` or `label.12` for metadata such as Updated 2 minutes ago.

Keep headings in order, so an `h2` follows the page's `h1`. Choose the element for its place in the outline and the set for its look, and don't skip levels to get a smaller size.

### Match Sets to Their Job

Use `copy` for anything that can wrap and `label` for single lines next to controls. Use `button` sets only inside buttons, since their weight and tracking are tuned for short labels on a filled shape.

Reserve `heading.40` and larger for hero numbers, such as a wallet balance or a TPS reading on a dashboard tile.

### Use Tabular Figures for Numbers

Proportional figures change width per digit, so amounts in a column don't line up and live values jitter as they update. `Amount`, `Table.Cell` with `numeric`, `NumberField`, and the `Slider` output already set tabular figures.

**Do:** Align amounts with tabular figures, such as `Amount` with `align="right"`.

**Don't:** Stack amounts in proportional figures, where digits drift out of line.

When you render a number yourself, such as block time, TPS, or a metric tile, add `fontVariantNumeric: 'tabular-nums !custom'` to its style.

### Use Monospace for Hashes and Code

Set addresses, transaction hashes, API keys, and code in a `.mono` set, so similar characters stay distinct and equal-length values align. `Hex`, `Snippet`, and `SecretValue` do this for you.

**Do:** Use `Hex` to keep both ends of an address visible when it truncates.

**Don't:** Cut an address off at the end, which hides its checksum characters.

Keep monospace for values. Labels, amounts, and prose stay in Geist.

### Keep Lines Readable

Keep running text to about 45 to 75 characters per line. A `maxWidth: '65ch !custom'` on a paragraph does this at any size. `Dialog` already caps its width at 28rem.

Avoid all caps for more than a word or two, and avoid centered paragraphs longer than two lines. Both slow reading.

### Write in Sentence Case

Use sentence case for headings, labels, and buttons, such as Recent activity and Add funds. Reserve capitals for proper nouns and product names, such as PathUSD and Tempo Wallet.

## Accessibility

* Keep body text at 4.5:1 contrast or more. See [Color](/docs/foundations/color) for measured pairs.
* Use real heading elements, in order. `AppShell.Title` renders the page's `h1`, and `Surface.Title` renders an `h2`.
* Make text resizable to 200% without loss of content. Avoid fixed heights on containers that hold text.
* Give truncated values a way to read them in full. `Hex` passes the full value to screen readers, and `copy` lets sighted readers take it.
* Set `lang` on the page, and on any passage in another language, so screen readers pronounce it correctly.

## Related

* [Color](/docs/foundations/color) for text colors and contrast
* [Amount](/docs/components/amount) for formatted, tabular amounts
* [Hex](/docs/components/hex) for addresses and hashes
* [Snippet](/docs/components/snippet) for commands and code
* [Blockchain Values](/docs/patterns/blockchain-values) for displaying onchain data
