# Typography

The typefaces, text sizes and named text styles that every component sets its words in.

Status: stable. Changes with viewport.

## Guidelines

### Purpose

The vocabulary of text: which faces exist, which sizes exist, and the named presets that combine face, size, weight, line height, letter spacing and case into a role such as page title, body copy or control label. Components consume presets. They do not compose text from raw values.

### How it is structured

Three groups, stacked.

At the bottom, the **typographic primitives**: the font families — a display face for headings, a body face for reading, and a monospace face for code and keys — plus runs of sizes, weights and letter-spacing steps. These are ingredients and reach nothing directly.

In the middle, the **text sizes**: a semantic run of named sizes for display, heading, body and label roles. This is the only layer that changes with the viewport, and it is the layer the presets read their size from.

On top, the **presets**: display registers and heading levels, each with a light version, body sizes with their medium, semibold and bold versions, label sizes, a small field size and code sizes. Each preset is one composite token. Labels are set in the body face at a medium weight, in sentence case. A preset set in capitals carries the case out of the composite as a setting the component applies, because the token standard's text recipe has no slot for it.

The shape follows role families — display, heading, body, label — with headings independent of the HTML heading level a page chooses.

Tabular figures, digits of equal width for columns, counters and timers, are a CSS setting on the component that needs them, not a token.

### Tiers

Text keeps its full line box, in Figma and in the components: nothing is trimmed to the cap height. The space between two texts is the gap between their boxes plus each line's own leading, so the text spacing tokens are set for untrimmed text, and a stack of text in the browser measures the same as its Figma frame.

Font families, sizes, weights and letter-spacing steps are primitives and do not reach CSS as custom properties, for the same reason primitive colors do not: the text ramp compresses at narrow widths, and a component reading a raw size would be blind to that. Semantic text sizes and the presets are what reach CSS. A preset becomes one custom property per part — family, size, weight, line height, letter spacing and text transform — because no single CSS property carries all of them without losing some.

### Context behavior

Text size follows the viewport and involves no choice. Below the switch width the text sizes take their minimum values; at and above it, the display and heading sizes step up, some by more than one step, while body and label sizes stay put. The switch width is itself a token, documented under Breakpoints, so the boundary is a design decision held in the token source rather than a number in a stylesheet.

Line heights are ratios, so they scale with the size on their own. Letter spacing is a fixed length, so it does not; the display and heading presets use no tracking, and only the smaller labels carry a little.

Theme and density do not touch typography.

### Usage rules

#### Do

- Consume a preset by referencing all of its parts together. Longhand properties are always safe; the label presets additionally need their text transform applied.
- Match the preset to the role, not the size. A card title is the preset that card titles use; if it needs to be bigger, that is a different preset, not a size override.
- Choose the HTML heading level from document structure and the preset from appearance. They are independent (TYP-001).
- Use the label presets for every uppercase moment. Uppercase outside the label register is not part of the system.
- Let text reflow and grow. Components must survive 200% text scaling and the guideline's text-spacing overrides without clipping (TYP-002, TYP-003).

#### Don't

- Write a font family, size, weight, line height or letter spacing in component CSS. The conformance checker rejects it.
- Apply uppercase to a body or display preset.
- Override one part of a preset with another preset's part. Mixing a heading's size with a body face is a new preset, and new presets are decided in Figma.
- Use a viewport media query on text inside a component. The text sizes already follow the viewport; a component that wants a different size at a different width wants a different preset.
- Leave any of the three faces unloaded. Each family names backups after it, a stand-in face sized to match and then the system's own font, so a page drawn before its face arrives keeps its layout. But the backups only stand in; they are not the system's type.
- Use a display preset more than once on a page, or in a product screen. Display presets are for one moment on a marketing or brand page; product screens stop at the largest heading.

## Tokens

### Font family

Primitive, 3 tokens.

| Token | CSS | Value |
|---|---|---|
| `font.family.display` | not in CSS | Funnel Display, Funnel Display Fallback, system-ui, sans-serif |
| `font.family.body` | not in CSS | Geist, Geist Fallback, system-ui, sans-serif |
| `font.family.mono` | not in CSS | Geist Mono, Geist Mono Fallback, ui-monospace, monospace |

### Font size

Primitive, 14 tokens.

| Token | CSS | Value |
|---|---|---|
| `font.size.12` | not in CSS | 12px |
| `font.size.14` | not in CSS | 14px |
| `font.size.16` | not in CSS | 16px |
| `font.size.18` | not in CSS | 18px |
| `font.size.20` | not in CSS | 20px |
| `font.size.24` | not in CSS | 24px |
| `font.size.28` | not in CSS | 28px |
| `font.size.32` | not in CSS | 32px |
| `font.size.40` | not in CSS | 40px |
| `font.size.48` | not in CSS | 48px |
| `font.size.56` | not in CSS | 56px |
| `font.size.64` | not in CSS | 64px |
| `font.size.72` | not in CSS | 72px |
| `font.size.88` | not in CSS | 88px |

### Font weight

Primitive, 4 tokens.

| Token | CSS | Value |
|---|---|---|
| `font.weight.light` | not in CSS | 300 |
| `font.weight.regular` | not in CSS | 400 |
| `font.weight.medium` | not in CSS | 500 |
| `font.weight.semibold` | not in CSS | 600 |

### Font letter spacing

Primitive, 2 tokens.

| Token | CSS | Value |
|---|---|---|
| `font.letter-spacing.none` | not in CSS | 0px |
| `font.letter-spacing.loose` | not in CSS | 0.5px |

### Text styles

Semantic, 36 tokens.

| Style | Size | Weight | Line height | Typeface | CSS |
|---|---|---|---|---|---|
| `typography.display-xl` | 56 to 88 px | 400 | 1 | Funnel Display | `--smbk-typography-display-xl-font-family`, `--smbk-typography-display-xl-font-size`, `--smbk-typography-display-xl-font-weight`, `--smbk-typography-display-xl-line-height`, `--smbk-typography-display-xl-letter-spacing`, `--smbk-typography-display-xl-text-transform` |
| `typography.display-lg` | 48 to 72 px | 400 | 1 | Funnel Display | `--smbk-typography-display-lg-font-family`, `--smbk-typography-display-lg-font-size`, `--smbk-typography-display-lg-font-weight`, `--smbk-typography-display-lg-line-height`, `--smbk-typography-display-lg-letter-spacing`, `--smbk-typography-display-lg-text-transform` |
| `typography.heading-xl` | 40 to 56 px | 400 | 1.2 | Funnel Display | `--smbk-typography-heading-xl-font-family`, `--smbk-typography-heading-xl-font-size`, `--smbk-typography-heading-xl-font-weight`, `--smbk-typography-heading-xl-line-height`, `--smbk-typography-heading-xl-letter-spacing`, `--smbk-typography-heading-xl-text-transform` |
| `typography.heading-lg` | 32 to 48 px | 400 | 1.2 | Funnel Display | `--smbk-typography-heading-lg-font-family`, `--smbk-typography-heading-lg-font-size`, `--smbk-typography-heading-lg-font-weight`, `--smbk-typography-heading-lg-line-height`, `--smbk-typography-heading-lg-letter-spacing`, `--smbk-typography-heading-lg-text-transform` |
| `typography.heading-md` | 28 to 40 px | 400 | 1.2 | Funnel Display | `--smbk-typography-heading-md-font-family`, `--smbk-typography-heading-md-font-size`, `--smbk-typography-heading-md-font-weight`, `--smbk-typography-heading-md-line-height`, `--smbk-typography-heading-md-letter-spacing`, `--smbk-typography-heading-md-text-transform` |
| `typography.heading-sm` | 24 to 32 px | 400 | 1.2 | Funnel Display | `--smbk-typography-heading-sm-font-family`, `--smbk-typography-heading-sm-font-size`, `--smbk-typography-heading-sm-font-weight`, `--smbk-typography-heading-sm-line-height`, `--smbk-typography-heading-sm-letter-spacing`, `--smbk-typography-heading-sm-text-transform` |
| `typography.heading-xs` | 20 to 28 px | 400 | 1.2 | Funnel Display | `--smbk-typography-heading-xs-font-family`, `--smbk-typography-heading-xs-font-size`, `--smbk-typography-heading-xs-font-weight`, `--smbk-typography-heading-xs-line-height`, `--smbk-typography-heading-xs-letter-spacing`, `--smbk-typography-heading-xs-text-transform` |
| `typography.heading-2xs` | 18 to 24 px | 400 | 1.2 | Funnel Display | `--smbk-typography-heading-2xs-font-family`, `--smbk-typography-heading-2xs-font-size`, `--smbk-typography-heading-2xs-font-weight`, `--smbk-typography-heading-2xs-line-height`, `--smbk-typography-heading-2xs-letter-spacing`, `--smbk-typography-heading-2xs-text-transform` |
| `typography.subtitle` | 20 px | 400 | 1.4 | Geist | `--smbk-typography-subtitle-font-family`, `--smbk-typography-subtitle-font-size`, `--smbk-typography-subtitle-font-weight`, `--smbk-typography-subtitle-line-height`, `--smbk-typography-subtitle-letter-spacing`, `--smbk-typography-subtitle-text-transform` |
| `typography.body-lg` | 20 px | 400 | 1.5 | Geist | `--smbk-typography-body-lg-font-family`, `--smbk-typography-body-lg-font-size`, `--smbk-typography-body-lg-font-weight`, `--smbk-typography-body-lg-line-height`, `--smbk-typography-body-lg-letter-spacing`, `--smbk-typography-body-lg-text-transform` |
| `typography.body-md` | 16 px | 400 | 1.5 | Geist | `--smbk-typography-body-md-font-family`, `--smbk-typography-body-md-font-size`, `--smbk-typography-body-md-font-weight`, `--smbk-typography-body-md-line-height`, `--smbk-typography-body-md-letter-spacing`, `--smbk-typography-body-md-text-transform` |
| `typography.body-sm` | 14 px | 400 | 1.5 | Geist | `--smbk-typography-body-sm-font-family`, `--smbk-typography-body-sm-font-size`, `--smbk-typography-body-sm-font-weight`, `--smbk-typography-body-sm-line-height`, `--smbk-typography-body-sm-letter-spacing`, `--smbk-typography-body-sm-text-transform` |
| `typography.body-xs` | 12 px | 400 | 1.5 | Geist | `--smbk-typography-body-xs-font-family`, `--smbk-typography-body-xs-font-size`, `--smbk-typography-body-xs-font-weight`, `--smbk-typography-body-xs-line-height`, `--smbk-typography-body-xs-letter-spacing`, `--smbk-typography-body-xs-text-transform` |
| `typography.label-lg` | 16 px | 500 | 1.4 | Geist | `--smbk-typography-label-lg-font-family`, `--smbk-typography-label-lg-font-size`, `--smbk-typography-label-lg-font-weight`, `--smbk-typography-label-lg-line-height`, `--smbk-typography-label-lg-letter-spacing`, `--smbk-typography-label-lg-text-transform` |
| `typography.label-md` | 14 px | 500 | 1.4 | Geist | `--smbk-typography-label-md-font-family`, `--smbk-typography-label-md-font-size`, `--smbk-typography-label-md-font-weight`, `--smbk-typography-label-md-line-height`, `--smbk-typography-label-md-letter-spacing`, `--smbk-typography-label-md-text-transform` |
| `typography.label-sm` | 12 px | 500 | 1.4 | Geist | `--smbk-typography-label-sm-font-family`, `--smbk-typography-label-sm-font-size`, `--smbk-typography-label-sm-font-weight`, `--smbk-typography-label-sm-line-height`, `--smbk-typography-label-sm-letter-spacing`, `--smbk-typography-label-sm-text-transform` |
| `typography.display-xl-light` | 56 to 88 px | 300 | 1 | Funnel Display | `--smbk-typography-display-xl-light-font-family`, `--smbk-typography-display-xl-light-font-size`, `--smbk-typography-display-xl-light-font-weight`, `--smbk-typography-display-xl-light-line-height`, `--smbk-typography-display-xl-light-letter-spacing`, `--smbk-typography-display-xl-light-text-transform` |
| `typography.display-lg-light` | 48 to 72 px | 300 | 1 | Funnel Display | `--smbk-typography-display-lg-light-font-family`, `--smbk-typography-display-lg-light-font-size`, `--smbk-typography-display-lg-light-font-weight`, `--smbk-typography-display-lg-light-line-height`, `--smbk-typography-display-lg-light-letter-spacing`, `--smbk-typography-display-lg-light-text-transform` |
| `typography.heading-xl-light` | 40 to 56 px | 300 | 1.2 | Funnel Display | `--smbk-typography-heading-xl-light-font-family`, `--smbk-typography-heading-xl-light-font-size`, `--smbk-typography-heading-xl-light-font-weight`, `--smbk-typography-heading-xl-light-line-height`, `--smbk-typography-heading-xl-light-letter-spacing`, `--smbk-typography-heading-xl-light-text-transform` |
| `typography.heading-lg-light` | 32 to 48 px | 300 | 1.2 | Funnel Display | `--smbk-typography-heading-lg-light-font-family`, `--smbk-typography-heading-lg-light-font-size`, `--smbk-typography-heading-lg-light-font-weight`, `--smbk-typography-heading-lg-light-line-height`, `--smbk-typography-heading-lg-light-letter-spacing`, `--smbk-typography-heading-lg-light-text-transform` |
| `typography.heading-md-light` | 28 to 40 px | 300 | 1.2 | Funnel Display | `--smbk-typography-heading-md-light-font-family`, `--smbk-typography-heading-md-light-font-size`, `--smbk-typography-heading-md-light-font-weight`, `--smbk-typography-heading-md-light-line-height`, `--smbk-typography-heading-md-light-letter-spacing`, `--smbk-typography-heading-md-light-text-transform` |
| `typography.heading-sm-light` | 24 to 32 px | 300 | 1.2 | Funnel Display | `--smbk-typography-heading-sm-light-font-family`, `--smbk-typography-heading-sm-light-font-size`, `--smbk-typography-heading-sm-light-font-weight`, `--smbk-typography-heading-sm-light-line-height`, `--smbk-typography-heading-sm-light-letter-spacing`, `--smbk-typography-heading-sm-light-text-transform` |
| `typography.heading-xs-light` | 20 to 28 px | 300 | 1.2 | Funnel Display | `--smbk-typography-heading-xs-light-font-family`, `--smbk-typography-heading-xs-light-font-size`, `--smbk-typography-heading-xs-light-font-weight`, `--smbk-typography-heading-xs-light-line-height`, `--smbk-typography-heading-xs-light-letter-spacing`, `--smbk-typography-heading-xs-light-text-transform` |
| `typography.heading-2xs-light` | 18 to 24 px | 300 | 1.2 | Funnel Display | `--smbk-typography-heading-2xs-light-font-family`, `--smbk-typography-heading-2xs-light-font-size`, `--smbk-typography-heading-2xs-light-font-weight`, `--smbk-typography-heading-2xs-light-line-height`, `--smbk-typography-heading-2xs-light-letter-spacing`, `--smbk-typography-heading-2xs-light-text-transform` |
| `typography.field-sm` | 16 px on a phone, 14 on a desktop | 400 | 1.5 | Geist | `--smbk-typography-field-sm-font-family`, `--smbk-typography-field-sm-font-size`, `--smbk-typography-field-sm-font-weight`, `--smbk-typography-field-sm-line-height`, `--smbk-typography-field-sm-letter-spacing`, `--smbk-typography-field-sm-text-transform` |
| `typography.code-sm` | 12 px | 400 | 1.5 | Geist Mono | `--smbk-typography-code-sm-font-family`, `--smbk-typography-code-sm-font-size`, `--smbk-typography-code-sm-font-weight`, `--smbk-typography-code-sm-line-height`, `--smbk-typography-code-sm-letter-spacing`, `--smbk-typography-code-sm-text-transform` |
| `typography.code-md` | 14 px | 400 | 1.6 | Geist Mono | `--smbk-typography-code-md-font-family`, `--smbk-typography-code-md-font-size`, `--smbk-typography-code-md-font-weight`, `--smbk-typography-code-md-line-height`, `--smbk-typography-code-md-letter-spacing`, `--smbk-typography-code-md-text-transform` |
| `typography.body-md-medium` | 16 px | 500 | 1.5 | Geist | `--smbk-typography-body-md-medium-font-family`, `--smbk-typography-body-md-medium-font-size`, `--smbk-typography-body-md-medium-font-weight`, `--smbk-typography-body-md-medium-line-height`, `--smbk-typography-body-md-medium-letter-spacing`, `--smbk-typography-body-md-medium-text-transform` |
| `typography.body-sm-medium` | 14 px | 500 | 1.5 | Geist | `--smbk-typography-body-sm-medium-font-family`, `--smbk-typography-body-sm-medium-font-size`, `--smbk-typography-body-sm-medium-font-weight`, `--smbk-typography-body-sm-medium-line-height`, `--smbk-typography-body-sm-medium-letter-spacing`, `--smbk-typography-body-sm-medium-text-transform` |
| `typography.body-lg-bold` | 20 px | 700 | 1.5 | Geist | `--smbk-typography-body-lg-bold-font-family`, `--smbk-typography-body-lg-bold-font-size`, `--smbk-typography-body-lg-bold-font-weight`, `--smbk-typography-body-lg-bold-line-height`, `--smbk-typography-body-lg-bold-letter-spacing`, `--smbk-typography-body-lg-bold-text-transform` |
| `typography.heading-2xs-bold` | 18 to 24 px | 700 | 1.2 | Funnel Display | `--smbk-typography-heading-2xs-bold-font-family`, `--smbk-typography-heading-2xs-bold-font-size`, `--smbk-typography-heading-2xs-bold-font-weight`, `--smbk-typography-heading-2xs-bold-line-height`, `--smbk-typography-heading-2xs-bold-letter-spacing`, `--smbk-typography-heading-2xs-bold-text-transform` |
| `typography.body-md-bold` | 16 px | 700 | 1.5 | Geist | `--smbk-typography-body-md-bold-font-family`, `--smbk-typography-body-md-bold-font-size`, `--smbk-typography-body-md-bold-font-weight`, `--smbk-typography-body-md-bold-line-height`, `--smbk-typography-body-md-bold-letter-spacing`, `--smbk-typography-body-md-bold-text-transform` |
| `typography.body-sm-bold` | 14 px | 700 | 1.5 | Geist | `--smbk-typography-body-sm-bold-font-family`, `--smbk-typography-body-sm-bold-font-size`, `--smbk-typography-body-sm-bold-font-weight`, `--smbk-typography-body-sm-bold-line-height`, `--smbk-typography-body-sm-bold-letter-spacing`, `--smbk-typography-body-sm-bold-text-transform` |
| `typography.body-md-semibold` | 16 px | 600 | 1.5 | Geist | `--smbk-typography-body-md-semibold-font-family`, `--smbk-typography-body-md-semibold-font-size`, `--smbk-typography-body-md-semibold-font-weight`, `--smbk-typography-body-md-semibold-line-height`, `--smbk-typography-body-md-semibold-letter-spacing`, `--smbk-typography-body-md-semibold-text-transform` |
| `typography.body-sm-semibold` | 14 px | 600 | 1.5 | Geist | `--smbk-typography-body-sm-semibold-font-family`, `--smbk-typography-body-sm-semibold-font-size`, `--smbk-typography-body-sm-semibold-font-weight`, `--smbk-typography-body-sm-semibold-line-height`, `--smbk-typography-body-sm-semibold-letter-spacing`, `--smbk-typography-body-sm-semibold-text-transform` |
| `typography.body-lg-light` | 20 px | 300 | 1.5 | Geist | `--smbk-typography-body-lg-light-font-family`, `--smbk-typography-body-lg-light-font-size`, `--smbk-typography-body-lg-light-font-weight`, `--smbk-typography-body-lg-light-line-height`, `--smbk-typography-body-lg-light-letter-spacing`, `--smbk-typography-body-lg-light-text-transform` |

### Font weight

Semantic, 1 token.

| Token | CSS | Value |
|---|---|---|
| `font.weight.bold` | not in CSS | 700 |

### Font fallback

Semantic, 30 tokens.

| Token | CSS | Value |
|---|---|---|
| `font.fallback.body.stack` | not in CSS | Geist Fallback, system-ui, sans-serif |
| `font.fallback.body.local` | not in CSS | Arial, Helvetica |
| `font.fallback.body.size-adjust` | not in CSS | 1.0264 |
| `font.fallback.body.ascent` | not in CSS | 0.9792 |
| `font.fallback.body.descent` | not in CSS | 0.2874 |
| `font.fallback.body.line-gap` | not in CSS | 0 |
| `font.fallback.body.light.size-adjust` | not in CSS | 1.0109 |
| `font.fallback.body.medium.size-adjust` | not in CSS | 1.0504 |
| `font.fallback.body.semibold.local` | not in CSS | Arial Bold, Helvetica Bold |
| `font.fallback.body.semibold.size-adjust` | not in CSS | 0.9956 |
| `font.fallback.body.bold.local` | not in CSS | Arial Bold, Helvetica Bold |
| `font.fallback.body.bold.size-adjust` | not in CSS | 1.0175 |
| `font.fallback.display.stack` | not in CSS | Funnel Display Fallback, system-ui, sans-serif |
| `font.fallback.display.local` | not in CSS | Arial, Helvetica |
| `font.fallback.display.size-adjust` | not in CSS | 1.0568 |
| `font.fallback.display.ascent` | not in CSS | 0.9463 |
| `font.fallback.display.descent` | not in CSS | 0.2366 |
| `font.fallback.display.line-gap` | not in CSS | 0 |
| `font.fallback.display.light.size-adjust` | not in CSS | 1.0481 |
| `font.fallback.display.medium.size-adjust` | not in CSS | 1.0685 |
| `font.fallback.display.semibold.local` | not in CSS | Arial Bold, Helvetica Bold |
| `font.fallback.display.semibold.size-adjust` | not in CSS | 0.9966 |
| `font.fallback.display.bold.local` | not in CSS | Arial Bold, Helvetica Bold |
| `font.fallback.display.bold.size-adjust` | not in CSS | 1.0044 |
| `font.fallback.mono.stack` | not in CSS | Geist Mono Fallback, ui-monospace, monospace |
| `font.fallback.mono.local` | not in CSS | Courier New, Courier |
| `font.fallback.mono.size-adjust` | not in CSS | 0.9998 |
| `font.fallback.mono.ascent` | not in CSS | 1.0052 |
| `font.fallback.mono.descent` | not in CSS | 0.295 |
| `font.fallback.mono.line-gap` | not in CSS | 0 |

### Text sizes

Semantic, 16 tokens.

| Token | CSS | Value |
|---|---|---|
| `text.display-xl` | `--smbk-text-display-xl` | min: 56px · max: 88px (changes with text-size) |
| `text.display-lg` | `--smbk-text-display-lg` | min: 48px · max: 72px (changes with text-size) |
| `text.heading-xl` | `--smbk-text-heading-xl` | min: 40px · max: 56px (changes with text-size) |
| `text.heading-lg` | `--smbk-text-heading-lg` | min: 32px · max: 48px (changes with text-size) |
| `text.heading-md` | `--smbk-text-heading-md` | min: 28px · max: 40px (changes with text-size) |
| `text.heading-sm` | `--smbk-text-heading-sm` | min: 24px · max: 32px (changes with text-size) |
| `text.heading-xs` | `--smbk-text-heading-xs` | min: 20px · max: 28px (changes with text-size) |
| `text.heading-2xs` | `--smbk-text-heading-2xs` | min: 18px · max: 24px (changes with text-size) |
| `text.body-lg` | `--smbk-text-body-lg` | 20px |
| `text.body-md` | `--smbk-text-body-md` | 16px |
| `text.body-sm` | `--smbk-text-body-sm` | 14px |
| `text.body-xs` | `--smbk-text-body-xs` | 12px |
| `text.label-lg` | `--smbk-text-label-lg` | 16px |
| `text.label-md` | `--smbk-text-label-md` | 14px |
| `text.label-sm` | `--smbk-text-label-sm` | 12px |
| `text.field-sm` | `--smbk-text-field-sm` | min: 16px · max: 14px (changes with text-size) |
