Getting started
Single-page view · as markdown
What is Trail
Trail / Getting started
Trail is a static site generator for documentation. You give it a directory of markdown files arranged into named folders; it gives you a complete website — every page rendered, cross-linked, indexed for search, mirrored as markdown for machines, and bundled into printable single-page views.
This site is built with it. Everything you are reading, including this page, comes out of one trail build.
The one idea: the tree is the site #
Trail has no table-of-contents file, no sidebar.json, no list of pages to keep in sync with the pages themselves. The directory tree is the navigation. A folder named 2--writing.topic is a topic called "Writing" that sorts second; the .md files inside it are its articles, in the order their own numeric prefixes give them.
learn/
├── trail.toml # the site
└── trail.product/ # a product
├── trail.toml
├── 1--getting-started.topic/ # a topic, sorted first
│ ├── 100--what-is-trail.md # an article, sorted first
│ └── 200--quick-start.md
└── 2--structuring-a-site.topic/
└── 100--products.md
Add a file, and the page exists, appears in the sidebar, joins the search index, and gets a markdown mirror. Rename a folder, and the section moves. Nothing else has to be told.
The trade is that names carry meaning. 100--quick-start.md is not a decorative filename: 100 is the sort key and quick-start is the URL segment. Directory and file names is the exhaustive grammar; Anatomy of a site is the guided tour.
Strict by default #
The second principle is that mistakes are build failures, not silent surprises. A documentation site that quietly drops a page is worse than one that refuses to build.
- A key that
trail.tomldoes not define is an error, not an ignored line. - A file trail does not recognise — a stray
notes.txtin a topic, aREADME.mdwithout an order prefix — is an error, not a file left out of the site. - A
~linkwhose target no longer exists fails the build. Every broken link in the site is reported at once, so one run tells you the whole story. - An ambiguous
~link— one matching two pages — always fails, even under--allow-dangling-links. Trail will not guess which page you meant. - A frontmatter
updated:that is not an ISO date, a colour that is not#rrggbb, a.linkalias pointing at another alias: all errors.
The escape hatches are deliberate and few. --allow-dangling-links downgrades missing targets to warnings while you are mid-refactor; that is the whole list.
One binary, no runtime #
Trail is a single Rust binary with everything compiled in: the templates, the stylesheet, the fonts and their licences, the search engine, the syntax highlighter, the diagram renderer. There is no theme directory to install beside it, no Node toolchain, no npm install step, and nothing to fetch at build time.
The output is equally plain. dist/ is a directory of static files — HTML, CSS, WOFF2, JSON, a WASM search core — that any static host will serve as-is. There is no server component and no build step at request time.
What one build does #
trail.toml + the tree
→ load parse every trail.toml and every frontmatter block, validate names
→ resolve .link aliases, ~link targets, inline-reference phrases, images
→ render one HTML page per article, product, anthology and book cover
→ export .md mirrors, /print bundles, llms.txt, site.json, sitemap, robots
→ index the Pagefind search bundle, built from the rendered pages
→ prune delete anything in dist/ this build did not write
Loading is where nearly all the errors live: by the time rendering starts, the site model is known to be well-formed. Pruning is why dist/ belongs entirely to trail — a page you deleted from the source disappears from the output on the next successful build, along with superseded search index fragments and stale images.
Builds are fast enough not to think about. This site — over twelve hundred pages — builds from scratch in a little over three seconds.
What a site is made of #
Four kinds of thing, from the outside in:
| What it is | |
|---|---|
| Site | One trail.toml at the root, the front page, and the products. |
| Product | A documented thing (trail.product/) with its own colour, monogram and landing page. |
| Grouping | How a product's pages are organised: anthologies, topics, books and shelves. |
| Article | One .md file: YAML frontmatter, then markdown. |
The distinction that matters most is topic versus book. A topic is a set of articles you can read in any order — a how-to section, a concept section. A book is a formal, ordered document — a specification, a reference manual — with a cover page, dotted section numbers (2.1.3), chapters that nest arbitrarily deep, and lettered appendices. They render differently because they are read differently.
What trail gives readers #
Beyond the pages themselves, every build produces:
- Search — a ⌘K modal over a Pagefind index, loaded lazily so pages carry no search cost until someone searches. Results carry the query through to the page, which highlights every match.
- Three-state theming — light, dark, or follow the system, remembered per reader. Syntax highlighting, diagrams and images all follow it.
- Print and machine views — every topic, book, anthology and product is also one long page at
/print, one markdown file at/print.md, and every individual page is mirrored at its own URL with.mdappended. - The reading furniture — a scroll-spying "On this page" list, copy buttons on code blocks, heading permalinks, adjustable text size, previous/next links, a mobile drawer, and a back-to-top button.
The reading experience covers all of it; The output surface covers exactly what lands in dist/.
What trail is not #
- Not a general-purpose SSG. There is no plugin system, no shortcode API, no way to add a page type. The templates are compiled in and are not user-replaceable; theming happens through CSS custom properties and an optional stylesheet.
- Not versioned. One build produces one version of the site. Hosting several versions means several builds at several paths.
- Not incremental. Every build renders every page. It is fast enough that this has never mattered.
- Not a subpath host. Sites are served from the root of a domain; internal links are root-relative.
Where to start #
If you want a working site in five minutes, go to the Quick start.
If you want to understand the layout before writing anything, read Anatomy of a site, then the Structuring a site section in order.
If you are writing pages in a site that already exists, Writing is the section you want — frontmatter, links, images, code blocks and the rest.
Quick start
Trail / Getting started
This page takes you from an empty directory to a served, searchable documentation site in about five minutes. You will write four files, run trail build, look at what it produced, then switch to trail serve and add a second page while it is running.
Prerequisites #
- A
trailbinary. Trail is a single Rust binary; build it from its source directory withcargo build --releaseand the result istarget/release/trail. Put it on yourPATH, or call it by path — the examples below assumetrailis on yourPATH. - Nothing else. The templates, stylesheet, fonts, syntax highlighter, diagram renderer and search engine are all compiled into the binary.
Create the site #
A trail site is a directory with a trail.toml in it. Make one:
$ mkdir acme-docs && cd acme-docs
# acme-docs/trail.toml
= "Acme Docs"
= "Documentation for Acme"
= "Guides and reference for the Acme toolchain."
= "Acme — docs."
Those four keys are the only required ones. sitename is the wordmark in the header (its last word is accented); title and description are the front page's headline and standfirst, and feed the site's <meta> tags. Everything else — the accent colour, the base URL, header links, a favicon — is optional and covered in Configuring the site.
Add a product #
Every page belongs to a product — a documented thing with its own landing page, colour and monogram. Products are directories named <slug>.product:
$ mkdir -p docs.product
# acme-docs/docs.product/trail.toml
= "Acme CLI"
= "ac"
= "#3b82f6"
= "The command-line interface to Acme."
All four keys are required. The directory name gives the URL (/docs) and the slug you link by; title is what readers see. The monogram is the two-or-so characters shown on the product's tile on the front page, and color tints the product's cards, sidebar and headings.
Add a topic and an article #
A topic is a set of articles read in any order. Topics are directories named <order>--<slug>.topic:
$ mkdir -p docs.product/1--guides.topic
Three things are going on in that filename and that frontmatter block:
1--guides.topic— the1sorts this topic among its siblings and never appears in a URL;guidesis the URL segment;.topicis the kind. With notrail.tomlof its own the topic's title is derived from the slug ("Guides").100--hello.md— same grammar for articles. Numbering in hundreds is a convention, not a rule: it leaves room to insert150--later without renumbering.title:andtype:are the two required frontmatter keys.description:is optional but worth writing — it becomes the page's meta description and its social-preview text.
Build it #
$ trail build
built 6 pages (1 products) → ./dist
Six pages from one article, because trail writes more than the pages you authored:
$ find dist -type f -not -path 'dist/assets/*' -not -path 'dist/pagefind/*'
dist/index.html the front page
dist/404.html the not-found page
dist/docs/index.html the product landing page
dist/docs/guides/hello/index.html your article
dist/docs/print/index.html all of Acme CLI on one page
dist/docs/guides/print/index.html all of Guides on one page
dist/docs.md the product, as markdown
dist/docs/guides/hello.md your article, as markdown
dist/docs/print.md all of Acme CLI, as one markdown file
dist/docs/guides/print.md all of Guides, as one markdown file
dist/llms.txt the site's map for language models
dist/site.json the site's structure, machine-readable
dist/robots.txt
Plus dist/assets/ (the stylesheet and fonts) and dist/pagefind/ (the search index and its WASM core). Note that there is no page at /docs/guides — a topic has no page of its own. It shows up as a card on the product page, and links to it land on its first article.
dist/ belongs to trail: at the end of every successful build it deletes anything in there that the build did not write.
Serve it #
$ trail serve
serving ./dist at http://127.0.0.1:8724 (watching . for changes)
trail serve builds the site, serves it, and watches the source tree. Save a file and the browser reloads itself; break something and the error prints in the terminal while the last good output keeps being served.
Open the site and try it: press ⌘K (or Ctrl K) to search, and use the theme button in the header to switch between light, dark and system.
Add a second page #
Leave the server running and add another article beside the first:
-
Two new things:
~docs/guides/hellois a page reference, not a URL. The first segment names a product; the rest matches the end of a page's path. Move or rename the target and the link still resolves as long as its slug is unchanged; delete it and the build fails rather than shipping a dead link. See Links and references.related:takes the same references (without the~) and renders a "Related content" list at the foot of the page.
Save, and the page appears in the sidebar next to Hello.
Where to go next #
- Anatomy of a site — the whole tree, level by level, with a real example.
- Products — the structuring section in order: products, anthologies, topics, books.
- Articles and frontmatter — every frontmatter key and what it does.
- Configuring the site — the base URL, accent colour, navigation links, edit links and the rest of
trail.toml.
Anatomy of a site
Trail / Getting started
A trail site is a directory tree in which every name means something. This page walks that tree from the root down, and explains the naming grammar all of it shares. Directory and file names is the same material as an exhaustive table; Structuring a site covers each kind of container in depth.
The whole shape #
learn/ the site root
├── trail.toml the site config — required
├── dist/ the build output — trail's to manage
│
├── peios.product/ a product → /peios
│ ├── trail.toml required: title, monogram, color, description
│ ├── 1--security-fundamentals.antho/ an anthology → /peios/security-fundamentals
│ │ ├── trail.toml required: title, description
│ │ ├── 1--identity.topic/ a topic → /peios/security-fundamentals/identity
│ │ │ ├── trail.toml optional: title, inline_ref
│ │ │ ├── 100--sids.md an article → …/identity/sids
│ │ │ ├── 200--principals.md
│ │ │ ├── 300--tokens/ a subfolder → …/identity/tokens/…
│ │ │ │ ├── trail.toml optional
│ │ │ │ └── 100--issuing.md → …/identity/tokens/issuing
│ │ │ └── diagram.svg an image, published beside its article
│ │ └── 2--pgss.book/ a book → /peios/security-fundamentals/pgss
│ │ ├── trail.toml required: title, description; optional: short
│ │ ├── 1--scope.md §1 → …/pgss/scope
│ │ ├── 2--model/ a chapter, §2
│ │ │ ├── trail.toml optional
│ │ │ └── 1--objects.md §2.1
│ │ └── a1--glossary.md Appendix A
│ └── 2--tooling.shelf/ a shelf — a heading, not a URL segment
│ └── 1--pekit.topic/ → /peios/pekit (the shelf adds nothing)
│
└── pekit.product/ another product → /pekit
Everything below the root is optional. A site with one product, one topic and one article is a complete site; this site's trail.product is exactly that shape, four levels shallower than peios.product.
The naming grammar #
Three name shapes appear throughout the tree.
| Shape | Example | Where |
|---|---|---|
<slug>.product | pekit.product | Only at the site root. |
<order>--<slug>.<kind> | 2--writing.topic | Anthologies, topics, books, shelves. |
<order>--<slug> | 300--tokens, 2--model | Topic subfolders and book chapters — no suffix. |
<order>--<slug>.md | 100--sids.md | Articles. |
<order>--<slug>.link | 3--faq.link | Aliases; see Link aliases. |
The parts:
<order>is a non-negative integer used only for sorting among siblings. It never appears in a URL or a title. Gaps are fine and normal — numbering articles100,200,300leaves room to insert. Two siblings sharing an order is an error, because the resulting page order would depend on nothing you can see.<slug>is lowercase kebab-case: ASCII lowercase letters, digits and hyphens, not starting or ending with a hyphen. It becomes the URL segment, and it is what you write in a~link.<kind>isantho,topic,bookorshelf.
Inside books, an order may also be written a<N>-- (a1--glossary.md). Those are appendices: lettered A, B, C instead of numbered, and sorted after every numbered sibling at their level. See Books.
URLs #
A page's URL is its product slug followed by the slug of every container between the product and the page — with two exceptions.
peios.product/1--security-fundamentals.antho/1--identity.topic/100--sids.md
→ /peios/security-fundamentals/identity/sids
The exceptions:
- A shelf contributes no segment. It is a heading on its parent's page, nothing more. A topic inside
2--tooling.shelf/lives at/peios/<topic>, exactly as if the shelf were not there. - Order prefixes contribute nothing.
1--identity.topicgives the segmentidentity.
Which containers have a page of their own is worth knowing, because it decides what you can link to:
| Has a page | Links to it land on | |
|---|---|---|
| Site | / | — |
| Product | /pekit | its own landing page |
| Anthology | /peios/security-fundamentals | its own landing page |
| Book | /peios/…/pgss (the cover) | the cover |
| Article | its own URL | itself |
| Topic | no page | (topics are not link targets) |
| Topic subfolder | no page | (not a link target, except from a .link) |
| Book chapter | no page | (not a link target) |
| Shelf | no page | (not a link target) |
Topics, subfolders and chapters are real structure — they appear in sidebars, cards and contents lists, and links from those surfaces open their first article — but they have no page you can address, so a ~link cannot point at one. Point at the article you actually mean.
Titles #
Titles come from three places, in order of precedence:
- An article's
title:frontmatter, which is required. - A container's
trail.toml, where thetitlekey is required for products, anthologies and books, and optional for topics, subfolders, chapters and shelves. - The slug, hyphens to spaces and each word capitalised:
the-two-gatesbecomes "The Two Gates".
The derived form is good enough surprisingly often; write a trail.toml when it is not — acronyms and casing it cannot guess ("Peiso", "The Console"), or when the container needs inline_ref phrases.
What may sit where #
Trail rejects anything it does not recognise, so it is worth knowing what each level tolerates.
| Directory | May contain |
|---|---|
| The site root | trail.toml, *.product/, the output directory, files named by the config |
| A product | trail.toml, .antho/, .topic/, .book/, .shelf/ |
| An anthology | trail.toml, .antho/, .topic/, .book/, .shelf/ |
| A shelf | trail.toml, the same as its parent minus shelves — a shelf never holds a shelf |
| A topic | trail.toml, .md articles, <order>--<slug>/ subfolders, .link files, images |
| A topic subfolder | trail.toml, .md articles, .link files, images |
| A book | trail.toml, .md articles, <order>--<slug>/ chapters, .link files, images |
| A book chapter | trail.toml, .md articles, <order>--<slug>/ chapters, .link files, images |
Only the article-holding levels take files: .md articles, .link aliases and images may sit in a topic, a subfolder, a book or a chapter, and nowhere else. A product, anthology or shelf directory holds trail.toml and subdirectories, full stop.
Images sit beside the articles that use them and are published at the URL of the directory they were found in; see Images.
Two slugs are reserved: print (every container publishes a /print page) may not be an article slug, and assets and pagefind may not be product slugs.
Ordering #
Siblings sort by their <order> prefix, ascending. Within a book, appendix entries (a<N>--) sort after every numbered entry at the same level, and they are lettered rather than numbered.
Products are the exception: they have no order prefix. They sort by the root config's featured list first, in that list's order, then everything else by title.
Where to go next #
- Products — the product level in depth, and when to add another one.
- Anthologies and shelves — grouping a large product.
- Topics and folders — the everyday container.
- Books — numbered, ordered documents with chapters and appendices.