# Typography

The two text primitives the theme's type ladder is spelled through, plus the blocks that are more than a font.

```slint
import { Heading, Body, TypographyTone, BodySize, Blockquote, InlineCode } from "@glint/components/typography.slint";
import { Tokens } from "@glint/theme/tokens.slint";

export component Demo inherits Window {
    width: 560px;
    height: 300px;
    background: Tokens.color-background;

    VerticalLayout {
        alignment: center;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            VerticalLayout {
                width: 440px;
                spacing: 12px;

                Heading {
                    level: 2;
                    text: "Typography in Glint";
                }

                Body {
                    text: "Glint typography maps semantic font sizes to responsive rem tokens.";
                    tone: TypographyTone.muted;
                }

                Blockquote {
                    text: "Design is not just what it looks like and feels like. Design is how it works.";
                }
            }
        }
    }
}
```

## Usage

```slint
import { Heading, Body, TypographyTone, BodySize, Blockquote, InlineCode, BulletList } from "@glint/components/typography.slint";
import { Tokens } from "@glint/theme/tokens.slint";

export component AppWindow inherits Window {
    width: 560px;
    height: 300px;
    background: Tokens.color-background;

    VerticalLayout {
        alignment: center;
        padding: 24px;
        spacing: 12px;

        Heading {
            level: 1;
            text: "Documentation Heading";
        }

        Body {
            text: "Standard paragraph copy set at 400 regular weight.";
        }
    }
}
```

Two primitives rather than one, and the split falls on the weight: `Heading` is the semibold ladder, `Body` the running-text one. They are not named `Text` because that name is Slint’s own — importing a component called `Text` shadows the built-in for the whole file that imports it, and Glint ships a barrel, so one such name would cost every consumer the built-in `Text` in any file that imports Glint.

Every step is a `rem` token in `Tokens` — `typography-display-size`, `typography-headline-size`, `typography-h1-size`, `typography-h2-size`, `typography-title-size` for the headings, and the `typography-body-*` family under them. `rem` is relative to the default font size, which is what makes the whole ladder follow a reader who has set a larger one: nothing here is a pixel count, so nothing here ignores that setting. Components never spell a size of their own — a private scale is a second scale disagreeing with the first.

`level` is a step on that ladder and nothing else. It changes how big a heading is drawn, not what a screen reader calls it: `Heading` inherits Slint’s `Text` and declares no `accessible-role`, so `level: 1` and `level: 5` announce identically.

## Examples

### Heading levels

Headings provide five structured levels (`level: 1` through `level: 5`), all rendered in semibold weight.

```slint
import { Heading } from "@glint/components/typography.slint";
import { Tokens } from "@glint/theme/tokens.slint";

export component Demo inherits Window {
    width: 560px;
    height: 280px;
    background: Tokens.color-background;

    VerticalLayout {
        alignment: center;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            VerticalLayout {
                width: 440px;
                spacing: 8px;

                Heading { level: 1; text: "Heading 1 (Display)"; }
                Heading { level: 2; text: "Heading 2 (Headline)"; }
                Heading { level: 3; text: "Heading 3 (H1)"; }
                Heading { level: 4; text: "Heading 4 (H2)"; }
                Heading { level: 5; text: "Heading 5 (Title)"; }
            }
        }
    }
}
```

### Body sizes

Body text supports five distinct size steps: `BodySize.lg`, `BodySize.default`, `BodySize.sm`, `BodySize.label` (medium weight), and `BodySize.caption`.

```slint
import { Body, BodySize } from "@glint/components/typography.slint";
import { Tokens } from "@glint/theme/tokens.slint";

export component Demo inherits Window {
    width: 560px;
    height: 260px;
    background: Tokens.color-background;

    VerticalLayout {
        alignment: center;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            VerticalLayout {
                width: 440px;
                spacing: 8px;

                Body { size: BodySize.lg; text: "Large body text"; }
                Body { size: BodySize.default; text: "Default body text"; }
                Body { size: BodySize.sm; text: "Small body text"; }
                Body { size: BodySize.label; text: "Medium-weight form label"; }
                Body { size: BodySize.caption; text: "Caption footnote text"; }
            }
        }
    }
}
```

### Typography tones

Both `Heading` and `Body` accept semantic color tones via `TypographyTone`: `default`, `muted`, `subtle`, `accent`, and `danger`.

```slint
import { Body, TypographyTone } from "@glint/components/typography.slint";
import { Tokens } from "@glint/theme/tokens.slint";

export component Demo inherits Window {
    width: 560px;
    height: 240px;
    background: Tokens.color-background;

    VerticalLayout {
        alignment: center;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            VerticalLayout {
                width: 440px;
                spacing: 8px;

                Body { tone: TypographyTone.default; text: "Default high-contrast foreground"; }
                Body { tone: TypographyTone.muted; text: "Muted secondary foreground"; }
                Body { tone: TypographyTone.subtle; text: "Subtle footnote foreground"; }
                Body { tone: TypographyTone.accent; text: "Accent primary color highlight"; }
                Body { tone: TypographyTone.danger; text: "Destructive danger warning"; }
            }
        }
    }
}
```

### Rich text blocks

Use `Blockquote`, `InlineCode`, and `BulletList` for formatted prose structures.

```slint
import { Blockquote, InlineCode, BulletList, Body } from "@glint/components/typography.slint";
import { Tokens } from "@glint/theme/tokens.slint";

export component Demo inherits Window {
    width: 560px;
    height: 280px;
    background: Tokens.color-background;

    VerticalLayout {
        alignment: center;
        padding: 24px;

        HorizontalLayout {
            alignment: center;

            VerticalLayout {
                width: 440px;
                spacing: 12px;

                Blockquote {
                    text: "Simplicity is prerequisite for reliability.";
                }

                HorizontalLayout {
                    spacing: 6px;
                    alignment: start;
                    Body { text: "Use"; }
                    InlineCode { text: "cargo build"; }
                    Body { text: "to compile your project."; }
                }

                BulletList {
                    items: [
                        "Type-safe design token access",
                        "Seamless dark and light theme switching",
                        "Full keyboard and accessibility support"
                    ];
                }
            }
        }
    }
}
```

## API Reference

### Heading

The five heading steps, all at semibold — the ladder the theme spells out. `level` picks the step, and it is a size rather than an outline level: it changes what a heading looks like, not what a screen reader calls it.

### Properties

| Property | Type                | Default                  | Description                                                                                                           |
| -------- | ------------------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------- |
| `level`  | `in int`            | `1`                      | Which step, 1 (largest) through 5 (smallest). Out-of-range levels take the smallest step rather than drawing nothing. |
| `tone`   | `in TypographyTone` | `TypographyTone.default` | Colour role — `default`, `muted`, `subtle`.                                                                           |

### Enums

| Enum             | Values                                           |
| ---------------- | ------------------------------------------------ |
| `TypographyTone` | `default`, `muted`, `subtle`, `accent`, `danger` |

### Body

Running text, at the five steps of the body ladder. `label` and `caption` are the two that are not simply smaller body: a label is set at medium weight, the way a form label is, and a caption is the smallest step the theme publishes.

### Properties

| Property | Type                | Default                  | Description                                             |
| -------- | ------------------- | ------------------------ | ------------------------------------------------------- |
| `size`   | `in BodySize`       | `BodySize.default`       | Which step — `lg`, `default`, `sm`, `label`, `caption`. |
| `tone`   | `in TypographyTone` | `TypographyTone.default` | Colour role — `default`, `muted`, `subtle`.             |

### Enums

| Enum             | Values                                           |
| ---------------- | ------------------------------------------------ |
| `BodySize`       | `lg`, `default`, `sm`, `label`, `caption`        |
| `TypographyTone` | `default`, `muted`, `subtle`, `accent`, `danger` |

### Blockquote

A quoted passage, with the rule down its leading edge.

### Properties

| Property | Type        | Default    | Description      |
| -------- | ----------- | ---------- | ---------------- |
| `text`   | `in string` | no default | The quoted text. |

### InlineCode

A run of code inside a sentence, on the muted surface.

### Properties

| Property | Type        | Default    | Description |
| -------- | ----------- | ---------- | ----------- |
| `text`   | `in string` | no default | The code.   |

### BulletList

A list of strings, each behind a bullet. The bullet is a mark rather than a word and is silenced in the accessibility tree, so what is announced is the item.

### Properties

| Property | Type          | Default    | Description                    |
| -------- | ------------- | ---------- | ------------------------------ |
| `items`  | `in [string]` | no default | One string per item, in order. |

## Accessibility

- **None of these carries a role.** `Heading` and `Body` inherit Slint’s `Text`; `Blockquote`, `InlineCode` and `BulletList` are containers that draw text descendants. None declares an `accessible-role`, so a heading is not a heading node, a `BulletList` is not a list node and a `Blockquote` is not a quote node. Their text is announced in layout order. What structure a reader gets comes from the landmarks around the prose — a named `region`, a `Dialog`, a `GroupBox` — not from these.
- **The bullet is silent.** `BulletList` draws its mark with `accessible-role: none`, so what is announced is the item and not a punctuation character in front of it.
- **Tone is a color, and color is not the message.** `TypographyTone.danger` and `muted` change nothing a screen reader hears. Where the tone is the point — an error, a disabled hint — the words have to carry it too, or a component that announces it does ([Field](/docs/components/field#accessibility) puts a validation message in the accessible description; ADR-0013).
- **Contrast is the palette’s, not this component’s.** These primitives take `Tokens.color-foreground` and its muted and subtle siblings; whether a given pairing clears a contrast bar is a property of the palette in use, and a custom palette ([Theming](/docs/theming#custom-palettes)) owns that answer for itself.
