---
title: Writing docs
description: How to add a page, put it in the navigation, preview it locally and get it through the build checks.
---

The docs are Markdown files in `docs/`, built by MkDocs into a static site.
Engineering details — the theme, the build and deployment — are in the
repository's `README.md`.

## Preview locally

```console
$ uv sync
$ uv run mkdocs serve
INFO    -  [21:04:11] Serving on http://127.0.0.1:8001/
```

Pages reload as you save.

## Add a page

<div class="steps" markdown>

### Create the file

Put it in the folder of its section. File and folder names become the URL:
`docs/guides/deploy-previews.md` is served at `/guides/deploy-previews/`.

### Add front matter

```yaml
---
title: Deploy previews
description: One sentence, under 160 characters, saying what the page lets the reader do.
---
```

`title` is the page heading — do not repeat it as a `#` heading in the body.
`description` is the lede under the title, the search-result summary and the
social-card text, so write it for someone deciding whether to read on.

Optional keys: `sidebar_title` (a shorter nav label), `status` (`new`, `beta` or
`deprecated`), `toc: false`, `image` (a social card) and `noindex: true`.

### Add it to the navigation

Navigation is explicit, in `mkdocs.yml`:

```yaml title="mkdocs.yml"
nav:
  - Guides:
      - Change an existing repository: guides/existing-repository.md
      - Deploy previews: guides/deploy-previews.md
```

Sections nest to any depth; the sidebar and mobile drawer follow. A page that
is not in `nav` fails the build.

### Build it the way CI does

```console
$ make check
```

This builds with `--strict` and then checks the generated site.

</div>

## Reuse a fragment

Text that must stay identical on several pages — a warning, a prerequisite —
lives once in `docs/_snippets/` and is included where it is needed:

```markdown
;--8<-- "sandbox-base-url.md"
```

The build fails if the file does not exist. Snippet files are not pages and never
appear in the navigation.

## What the checks catch

| Check | Fails on |
|---|---|
| `mkdocs build --strict` | Broken links and anchors, pages missing from `nav`, root-absolute links, invalid config. |
| `scripts/check_site.py` | Markdown that did not render (a stray `!!!` or fence), missing titles or descriptions, duplicate IDs, broken asset or navigation links, images without alt text. |

## Style

- **Lead with the task.** Headings name what the reader is doing — "Set the
  budget", not "Budget configuration".
- **Link relatively to the Markdown file:** `[Previews](../developer/previews.md)`.
  Never `/developer/previews/` — that breaks under versioned URLs.
- **Show real values.** Field names, defaults and status codes come from the
  API, not from memory. When a behaviour depends on configuration, say so.
- **One callout per screen at most.** See [Components](components.md) for every
  building block.
