> For the complete documentation index, see [llms.txt](https://ascribed-dev.com/docs/llms.txt). This documentation describes Ascribe 0.4.4. Anything marked in main, not yet released isn't in that release yet.

# `ascribe.toml`: what pages hold

> Reference · The content model's note types, phrases, glossary, images, and widgets: what a page can hold beyond its type's fields.

Part of the [`ascribe.toml` reference](https://ascribed-dev.com/docs/content-model.md), whose [contents](https://ascribed-dev.com/docs/content-model.md#contents) list every section. The rules for a loader are in the contract's [what pages hold](https://ascribed-dev.com/docs/contracts/content-model/content.md).

## 6\. `[notes.<type>]`

Note types ([SPEC §4.5](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#45-note)), the values of `@note`'s `type` attribute. Five are built in: `note`, `tip`, `important`, `warning`, and `caution`. A project adds types by declaring them, and can relabel a built-in type. Built-in types can't be removed.

```toml
[notes.security]
label = "Security"

[notes.tip]
label = "Pro tip"
```

Tested with ascribe 0.4.4 on Linux.

`<type>` is the note type (`key` rule).

| Key | Type | Default | Description |
| --- | --- | --- | --- |
| `label` | string | built-in types: `Note`, `Tip`, `Important`, `Warning`, `Caution`; new types: **required** | The display label, used where a note's type is shown as text, such as the plain-markdown output's `**Tip: …**` ([SPEC §9.4](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#94-outputs)). |

Under `[notes]`, the inline form `security = { label = "Security" }` is equivalent.

---

## 7\. `[phrases]`

The phrases registry ([SPEC §5.1](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#51-phrases)).

```toml
[phrases]
product = "Quill"
cloud = "Quill Cloud"
version = "3.4.1"
api = "https://api.quill.dev/v3/"
support-email = "support@quill.dev"
# The current line of [releases.self-managed]: "3.5".
release = { line = "self-managed" }
```

Tested with ascribe 0.4.4 on Linux.

Every key is a phrase key (`key` rule, the same rule as SPEC Appendix A's `phrase`), and every value is a TOML string (`model-phrase-value-type`). `version = 3.4` is a float and an error; that's the kind of silent type change TOML exists to prevent ([SPEC §7.1](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#71-role)). Values are inserted as literal text ([SPEC §5.1](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#51-phrases)).

**A target's line.** A value can instead be a table with one key, `line`, naming a target whose lines `[releases.<target>]` (§12.1) lists: `release = { line = "self-managed" }`. The phrase is then a line as the list writes it: in the output of one line of a build the target versions (`[builds.<name>] versions`), that line, and everywhere else the target's current line, its newest live one. So it changes when a line moves to `live`, and has no second home to keep. The target needs a live line (`model-phrase-line`), and the table takes no other key (`model-unknown-key`).

**Phrases in frontmatter.** [SPEC §5.1](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#51-phrases) lets the [content model](https://ascribed-dev.com/docs/glossary.md#content-model) decide which frontmatter fields accept phrases. A field accepts them when its table form sets `phrases = true` (§5.2):

```toml
[types.guide.frontmatter]
title = { type = "string", phrases = true }
```

Only `string` and `list(string)` fields can accept phrases (`model-phrases-field-type`). By default, no field does. The setting lives on the field, not in `[phrases]`, so that a phrase key can never collide with a setting's name.

---

## 8\. `[glossary]`

The glossary ([SPEC §5.4](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#54-glossary-terms)): the pages that hold its terms, and how occurrences are matched. A term is a heading on a glossary page, marked [`@term`](https://ascribed-dev.com/docs/writing/directives.md#term), and its definition is the paragraph under it. Authors don't mark terms where they're used; processors link them ([SPEC §9.2](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#92-resolution) step 7).

```toml
[glossary]
match = "first"
case-sensitive = false
pages = ["reference/glossary.md"]
```

Tested with ascribe 0.4.4 on Linux.

### 8\.1 Settings

| Key | Type | Default | Description |
| --- | --- | --- | --- |
| `pages` | array of strings (paths) | `[]` | The glossary pages, each a path from the content root, written as in a link ([SPEC §5.2](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#52-links); a leading `/` is allowed and means the same). Each MUST be a page, not a [fragment](https://ascribed-dev.com/docs/glossary.md#fragment). A term is linked to the first of its pages that a build publishes. |
| `match` | string | `"first"` | Which occurrences are linked: `"first"`, the first occurrence of each term on each page, or `"every"`, every occurrence. "Page" means the resolved page for a build, after includes and build modes. A term can override it. |
| `case-sensitive` | boolean | `false` | Whether occurrences must match a term's case exactly. When `false`, matching ignores case. A term can override it. |

There's no `terms` table: a project that has one gets an error saying to write each term on a glossary page.

### 8\.2 Terms

Each heading on a glossary page with `@term` directly under it, written on the page or in a fragment it includes, is a term:

```markdown
## Rollout
@id: rollout
@term {aliases=rollouts}

A plan for turning a flag on for a growing share of users: by percentage, by segment, or on a schedule.
```

- **The term** is the heading's text, and **its id** is the heading's id, which the site output's `data-ascribe-term` names. An `@id` keeps the id when the heading's text changes.
- **The definition** is the paragraph that opens the section, after the heading's directives, as plain text, with the build's phrases. A paragraph a fragment brings in counts, and a section that opens with a `@variant` group has the paragraph each arm opens with, in the builds that keep the arm. The site output shows it as the link's title, and the editor on hover; the JSON output lists it with each use. Keep it to a sentence or two, and put the rest in a second paragraph.
- **A term follows its section in each build.** A build that removes the section, by `@available` or by a variant, has no such term, and a build that removes only its paragraph leaves it without a definition: either way, the build doesn't link it.
- `@term`'s attributes override the glossary's settings for the term:

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `aliases` | string | none | Other forms that count as occurrences, such as plurals, separated by `\|`: `aliases="API keys\|keys"`. |
| `case-sensitive` | boolean | the `[glossary]` setting | For terms like `Go` that collide with ordinary words. |
| `match` | `first`, `every`, or `marked` | the `[glossary]` setting | A `marked` term isn't linked automatically at all, for words like `build` that also have ordinary senses: link the occurrences that mean the term to its heading, as `[build](glossary.md#build)`. `marked` is a term setting only; `[glossary]` doesn't accept it. |

An unused term (`glossary-term-unused`) is acknowledged with [`@intended`](https://ascribed-dev.com/docs/writing/directives.md#intended) among the directives under its heading.

**A link to a term's heading is a use of the term,** wherever it's written: it gets the term's marker and its definition as its title, as an automatic link does, and it counts as a use. For a term with `match = "first"`, it's the page's first link to the term, so no later occurrence is linked. A link to the glossary page, or to another heading in the term's section, is an ordinary link.

To set one up, see [Set up a glossary](https://ascribed-dev.com/docs/content-model/glossary.md).

**Matching:** a term is found as a whole word, never inside one, and where two terms overlap, as `API key` and `API` do, the longer is linked. Only prose is searched, so a term in a heading, link text, code, raw HTML, or a directive's primary identifier stays as it is. A glossary page doesn't link the terms it shows, and a build doesn't link a term whose heading it leaves out, or that has no definition in it.

**Rules.** A path in `pages` names a page that's there, with its name's case as it is on disk (`model-glossary-page`). The rest are checked with the project, since terms are read from pages:

- Two terms or aliases with the same text, compared ignoring case unless both terms are case-sensitive, are reported at the second, which isn't a term (`glossary-term-duplicate`); so are two terms with the same id, which two headings with the same text on different pages give them.
- A term's definition is the paragraph that opens its section, so a term whose section opens with anything else has none (`glossary-definition-missing`). One over 300 characters in a build, the limit a page's `description` has, is advice (`glossary-definition-long`).
- `@term` on a page `pages` doesn't list, or in a fragment no such page includes, isn't a term (`term-off-glossary-page`). Its fix lists the page in `pages`; the editor applies it, and `ascribe check --fix` doesn't, since it links the page's terms on every page.

---

## 9\. `[images]`

Image attributes ([SPEC §5.3](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#53-images)): which keys may appear in an attribute block after an image, and their types.

```toml
[images.attributes]
width = "number?"
height = "number?"
loading = { type = "enum(lazy, eager)", default = "lazy" }
theme = "set(enum(light, dark))?"
```

Tested with ascribe 0.4.4 on Linux.

| Key | Type | Default | Description |
| --- | --- | --- | --- |
| `attributes` | table of attribute types | `{}` | Accepted image attribute keys (`key` rule) and their types (§5). With no entries, an image accepts no attributes, and any attribute block after an image is an error ([SPEC §8.2](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#82-diagnostics), "Unknown key"). Declaration order is canonical order ([SPEC §8.3](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#83-canonical-form)). A declared default reaches the output: in the site output, every image carries the defaults of attributes it doesn't write. |

Alt text and titles aren't attributes; they use CommonMark's syntax ([SPEC §5.3](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#53-images)). Presentation choices aren't image attributes either. Keys HTML already uses on `<img>` (`src`, `alt`, `title`, and its global attributes) are rejected (`model-attribute-reserved`), since the site output writes image attributes onto the `<img>` element ([SPEC §7.2](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#72-declarations)).

---

## 10\. `[widgets.<name>]`

Project widgets (SPEC §6): directives a documentation set defines. Each declaration is a directive schema with the same parts a built-in directive's schema has (SPEC §3, §4).

```toml
[widgets.quill-labspace]
description = "An embedded, runnable Quill lab."
forms = ["line"]
primary = "none"
binding = "self"
title = "none"
groupable = false
plain-fallback = "Try this in the Quill lab at labs.quill.dev."

[widgets.quill-labspace.attributes]
lab = "string"
height = "number?"
```

Tested with ascribe 0.4.4 on Linux.

`<name>` is the widget's name (SPEC A `widget-name`: lowercase, with at least one hyphen). Names starting with `ascribe-` are reserved for Ascribe's element library, and the names HTML reserves for itself (`annotation-xml`, `color-profile`, `font-face`, `font-face-src`, `font-face-uri`, `font-face-format`, `font-face-name`, `missing-glyph`) aren't allowed, because the site output emits a widget as a custom element with the widget's name ([SPEC §9.4](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#94-outputs)).

| Key | Type | Default | Description |
| --- | --- | --- | --- |
| `forms` | array of strings | **required** | The permitted forms ([SPEC §3.5](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#35-forms)): `["line"]`, `["container"]`, or both. |
| `primary` | string | `"none"` | The primary ([SPEC §3.4](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#34-the-primary)): `"none"`; `"identifier"` or `"text"`, which are required; or `"identifier?"` or `"text?"`, which are optional. |
| `binding` | string | required when `forms` includes `"line"`; not allowed otherwise | What the line form applies to ([SPEC §3.8](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#38-binding)), one of the values below. |
| `title` | string | `"none"` | Whether the widget takes a title line ([SPEC §3.7](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#37-titles)): `"none"`, `"accepted"`, or `"required"`. |
| `groupable` | boolean | `false` | Whether a run of the widget's openers forms a group of arms ([SPEC §3.6](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#36-groups)). |
| `attributes` | table of attribute types | `{}` | The attribute schema: keys (`key` rule) and types (§5). Declaration order is canonical order ([SPEC §8.3](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#83-canonical-form)). The keys `heading` and `primary`, HTML's global attributes (including `title`), `aria-` keys, and event-handler attributes such as `onclick`, are rejected (`model-attribute-reserved`): the site output writes these attributes onto the widget's element ([SPEC §7.2](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#72-declarations)). |
| `plain-fallback` | string | none | Plain-text fallback for the plain-markdown output (SPEC §6, §9.4): CommonMark text written in place of the widget. Phrases in it are substituted. It isn't a template: attribute values aren't inserted. Without it, the widget itself emits nothing. |
| `plain-content` | string | `"keep"` | For a widget that wraps content, whether the plain-markdown output keeps that content (`"keep"`) after the fallback, or drops it (`"drop"`). Allowed only when the widget wraps content: it has container form, or its binding is `block` or `heading-or-block`. |
| `description` | string | none | Help text for the editor's hover and completion. |

**Binding values:**

| Value | [SPEC §3.8](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#38-binding) binding | Behaves like |
| --- | --- | --- |
| `"self"` | Self: its own primary, or nothing | `@include` |
| `"heading"` | Preceding heading: at the top of its section | `@id` |
| `"block"` | Following block. If the primary is a text primary and it's given, the primary is the content and nothing else is bound | `@steps`; with `primary = "text?"`, `@note` |
| `"heading-or-block"` | At the top of a section, the section; anywhere else, the following block | `@available` |

**Rules:**

- A widget with container form can't have a required primary, since a container opener's primary is empty ([SPEC §3.5](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#35-forms)), and a container-only widget's primary is `"none"` (`model-widget-container-primary`).
- A groupable widget is container-only, since the arms of a group are containers ([SPEC §3.6](https://github.com/ascribed-dev/ascribe/blob/main/SPEC.md#36-groups)) (`model-widget-groupable-form`).
- A widget with line form needs a `binding`, and one without it can't have one (`model-widget-binding`).

What the site output writes for a widget, its element's tag name and attributes, is set by the [element contract](https://github.com/ascribed-dev/ascribe/blob/main/packages/elements/CONTRACT.md).

What a widget's attributes mean is the site's to check: Ascribe doesn't know that an attribute names a page, so it doesn't check that the page exists. Ascribe's own landing page is laid out with widgets that [`site/home/ascribe.toml`](https://github.com/ascribed-dev/ascribe/blob/main/site/home/ascribe.toml) declares.
