Structuring a site
Single-page view · as markdown
Products
Trail / Structuring a site
A product is the outermost division of a trail site: one documented thing, with its own landing page, its own colour, and its own URL prefix. Products are the only children the site root will accept, so every page in the site belongs to exactly one.
On this site, pekit, provium and trail are products. So is peios itself — products are not necessarily software packages, they are simply the largest chunk of documentation a reader would think of as one subject.
The directory #
A product is a directory named <slug>.product at the site root. It has no order prefix.
learn/
├── trail.toml
├── pekit.product/ → /pekit
└── trail.product/ → /trail
The slug becomes the URL prefix (/pekit) and the first segment of every ~link into that product. It must be lowercase kebab-case, and it may not be assets or pagefind — those collide with the output directories trail writes.
The config #
Every product needs a trail.toml, and all four keys are required:
# pekit.product/trail.toml
= "pekit"
= "pk"
= "#22c55e"
= "The recipe-driven build and packaging tool — declarative recipes in; built, tested, packaged software out."
| Key | |
|---|---|
title | The product's display name, used everywhere the product is named. Unlike other containers, this is not derived from the slug — a product always states it. |
monogram | A very short string — usually two characters — shown on the product's tile, on the front-page card and at the top of the product page. Initials or a shortened name. |
color | A #rrggbb hex colour. Any other form is a build error. |
description | One sentence, shown on the front-page card, under the product's name on its own page, and in llms.txt. It is also the product page's meta description. |
One optional key, inline_ref, claims phrases that auto-link to the product page wherever they appear in prose; see Inline references.
What the colour does #
color is the product's accent, and by default it displaces the site accent on every page belonging to that product: card borders and hover states, the tile, sidebar highlights, heading numbers, RFC-2119 keywords, focus rings.
Setting product_theming = false in the root trail.toml turns this off site-wide, and everything tints from the site accent instead. Products still keep their monogram and card, they just stop colouring their pages. See Theming.
What a product contains #
A product directory holds its trail.toml and nothing but grouping directories:
- Anthologies (
.antho) — a titled landing page with its own children, for products big enough to need a second level. - Topics (
.topic) — a set of articles read in any order. The everyday container. - Books (
.book) — a formal, ordered document with a cover page, numbered sections and appendices. - Shelves (
.shelf) — a titled heading grouping some of the above on the product page. No URL segment of its own.
Articles, images and .link files may not sit directly in a product directory; they live inside topics and books. A product with nothing in it is legal and simply renders an empty landing page.
Small products go straight to topics:
pekit.product/
├── trail.toml
├── 1--getting-started.topic/
├── 2--recipes.topic/
├── 3--running.topic/
└── 4--reference.topic/
Large ones add a layer:
peios.product/
├── trail.toml
├── 1--security-fundamentals.antho/
├── 2--using-peios.antho/
├── 3--advanced-peios.antho/
├── 4--developing-for-peios.antho/
└── 5--peiso.antho/
Both shapes are ordinary. Reach for an anthology when a product's topic list has grown long enough that readers have to hunt through it.
The product page #
Every product gets a landing page at /<slug>, built from the config and its children:
- A hero with the monogram tile, the title and the description.
- A "Documentation" section listing every child as a card. Anthologies and books get a card with their description; topics get a card listing their articles — all of them when there are four or fewer, otherwise the first three and a "See all N articles" link — with a count and the topic's total reading time.
- Each shelf becomes a subheading above its own row of cards. Runs of items outside any shelf render as an untitled row, in place.
Ordering #
Products have no order prefix. They sort by the root trail.toml's featured list — in that list's order — and then everything not listed, by title.
# learn/trail.toml
= ["peios", "pekit", "provium", "universal-directory", "trail"]
That list does more than sort. The front page shows only featured products. An unfeatured product is still built, still searchable, still linkable, and still in the header's products menu — it just does not get a card on the front page. Naming a product that does not exist in featured is a build error.
Roll-ups #
A product totals up what is beneath it. Its reading time is the sum of every article's, and its updated date is the most recent updated: in the whole product. Anthologies, topics, books and chapters do the same at their own level. See Articles and frontmatter.
When to add a product #
A product is a heavy division: its own colour, its own landing page, its own URL prefix, and a separate card on the front page. Add one when readers would go looking for the subject by name and would not expect to find it filed under something else.
Within a single product, the lighter divisions — anthologies for a second level of landing pages, shelves for a heading, topics for a set of articles — are almost always the better answer.
Anthologies and shelves
Trail / Structuring a site
Once a product has more than a handful of topics, its landing page becomes a wall of cards. Trail offers two ways to break that up, and the difference between them is exactly one thing: whether the grouping gets a page of its own.
| URL segment | Page | Sidebar | Nests | |
|---|---|---|---|---|
Anthology (.antho) | yes | its own landing page | — | in products and anthologies |
Shelf (.shelf) | no | no — a heading on its parent's page | — | in products and anthologies, never in a shelf |
Reach for a shelf when the topics belong together but readers should still see them all at once. Reach for an anthology when the group is big enough to deserve its own front door.
Anthologies #
An anthology is a directory named <order>--<slug>.antho. It has a landing page at its own URL, listing everything inside it exactly as a product page lists its own children.
peios.product/
├── trail.toml
├── 1--security-fundamentals.antho/ → /peios/security-fundamentals
│ ├── trail.toml
│ ├── 1--identity.topic/ → /peios/security-fundamentals/identity
│ └── 2--pgss.book/ → /peios/security-fundamentals/pgss
└── 2--using-peios.antho/ → /peios/using-peios
Its trail.toml requires two keys:
# 1--security-fundamentals.antho/trail.toml
= "Security fundamentals"
= "How Peios decides who may do what: identity, tokens, and the two gates every request passes."
title is the heading and the breadcrumb; description is the standfirst on the page, the text on the card that links to it from the product page, and the entry in llms.txt. The optional inline_ref key claims phrases pointing at this anthology's page; see Inline references.
An anthology may contain anthologies, topics, books and shelves — the same set a product may contain. Anthologies nesting inside anthologies is legal and unbounded, and each level adds a URL segment:
/peios/developing-for-peios/apis/filesystem/open
└─ anthology ──────┘ └ antho ┘ └ topic ─┘ └ article
In practice two levels is plenty. Deeper than that and readers stop being able to guess where anything lives.
The anthology page #
The page mirrors a product page one level down: a breadcrumb trail back to the product (and through any parent anthologies), the title, the description, and a "Topics" section of cards. Nested anthologies and books get a description card; topics get a card listing their articles.
Shelves #
A shelf is a directory named <order>--<slug>.shelf. It is presentation only: a shelf contributes no URL segment, has no page, and never appears in a breadcrumb. Its whole job is to put a subheading above a row of cards on the page it sits in.
peios.product/
└── 3--tooling.shelf/ (no URL of its own)
├── trail.toml
├── 1--pekit.topic/ → /peios/pekit
└── 2--provium.topic/ → /peios/provium
Note the URLs: the topics live directly under the product, exactly as they would if the shelf directory were not there.
Its trail.toml is optional and carries at most a title:
# 3--tooling.shelf/trail.toml
= "Tooling"
Without one, the title is derived from the slug — 3--tooling.shelf becomes "Tooling" anyway, so most shelves need no config at all. A shelf may not declare inline_ref: it has no page to link to, so the phrase would have nowhere to land. Declare it on the shelf's items instead.
How shelves render #
On the parent's page, children render in order. A run of items outside any shelf renders as an untitled row of cards; each shelf renders as its title followed by its own row:
Documentation
[ Getting started ] [ Concepts ] ← loose items, no heading
Tooling ← shelf title
[ pekit ] [ provium ]
Specifications ← another shelf
[ PGSS ] [ PCDS ]
Because a shelf's items share their parent's URL space, their slugs must be unique across the whole parent — two shelves cannot both contain a getting-started topic, since both would want /peios/getting-started. Trail rejects that at build time.
The rules #
- A shelf may sit in a product or an anthology.
- A shelf may contain anthologies, topics and books.
- A shelf may never contain another shelf. Nested headings on a card grid are not a structure worth having; if you need the second level, use an anthology.
- A shelf directory holds
trail.tomland subdirectories only — no articles, no images.
Choosing #
Start flat. A product with four or five topics needs neither of these.
When the list gets long, ask whether the groups are worth a page. "Tooling" as a heading over two topic cards is a shelf. "Security fundamentals" — nine topics, two specifications, and a description worth reading — is an anthology.
The cost of an anthology is a URL segment on every page beneath it and one more click for the reader. The cost of a shelf is nothing at all, which is why it is usually the right first move.
Topics and folders
Trail / Structuring a site
A topic is a set of articles on one subject, read in any order. It is the container most pages live in, and the one to reach for unless you specifically need a book.
"Getting started", "Concepts", "Configuration", "Reference" — those are topics. The reader arrives at one of the articles from search or a link, reads it, and moves on; the sidebar is there for when they want to see what else is nearby.
The directory #
A topic is a directory named <order>--<slug>.topic, sitting in a product, an anthology or a shelf. Its articles are the .md files inside it:
4--reference.topic/ (the topic; no page of its own)
├── trail.toml optional
├── 100--recipe-format.md → /pekit/reference/recipe-format
├── 200--supporting-files.md → /pekit/reference/supporting-files
└── 300--cli.md → /pekit/reference/cli
The 4 orders this topic among its siblings; reference is the URL segment. Articles carry the same <order>--<slug> grammar, and their order prefixes decide reading order — the sidebar, the previous/next links, and the order they appear in the topic's single-page view.
Numbering in hundreds is the convention across this site. It is not a rule — 1--, 2--, 3-- works identically — but it means you can insert 150-- between two articles without renaming anything.
The config #
A topic's trail.toml is optional, and holds at most two keys:
# 4--reference.topic/trail.toml
= "Reference"
Without it, the title is derived from the slug: 4--reference.topic becomes "Reference", 2--writing-tests.topic becomes "Writing tests". Write the file when that derivation is wrong — an acronym, a proper noun, unusual casing — or when the topic needs inline_ref phrases, which claim text that should auto-link to the topic's first article. See Inline references.
A topic has no page #
This is the one surprising thing about topics. There is no page at /pekit/reference — the topic is not a destination, it is a grouping.
That has three consequences:
- A topic appears as a card on its product's or anthology's page, listing its articles (all of them when there are four or fewer, otherwise the first three plus a "See all" link) with a count and its total reading time.
- Links land on the first article. Every surface that shows a topic — its card heading, its "See all" link, the sidebar heading on an article page — points at the topic's first article in reading order.
- A topic is not a
~linktarget.~pekit/referenceresolves nothing, becausereferenceis not the last segment of any page's path. Link to the article you mean:~pekit/reference/cli.
What does exist at the topic's path is its single-page view: /pekit/reference/print renders every article in the topic as one long page, and /pekit/reference/print.md is the same thing as markdown. See The output surface.
Subfolders #
A topic that has grown past a comfortable sidebar can group runs of articles into subfolders — plain <order>--<slug> directories, with no type suffix:
2--concepts.topic/
├── 100--objects-and-identity.md → /ud/concepts/objects-and-identity
├── 200--the-schema-model.md → /ud/concepts/the-schema-model
└── 300--replication/ a subfolder
├── trail.toml optional
├── 100--topology.md → /ud/concepts/replication/topology
└── 200--conflicts.md → /ud/concepts/replication/conflicts
A subfolder:
- adds a URL segment, unlike a shelf;
- has no page of its own, like a topic — links from the sidebar open its first article;
- renders as a collapsible group in the sidebar, expanded automatically when the article being read is inside it;
- may hold
trail.toml, articles,.linkfiles and images — but not another subfolder. Subfolders are exactly one level deep. A topic needing more nesting than that is either two topics or a book.
Its optional trail.toml takes the same title and inline_ref keys a topic's does, and its title derives from its slug the same way.
An empty subfolder is tolerated and simply not shown, so you can create the directory before its articles exist.
The sidebar #
On an article page, the sidebar shows the article's own topic: the topic title (linking to its first article) above the topic's contents in reading order, with subfolders as collapsible groups and the current article marked. It shows one topic — not the whole product — because the topic is the unit a reader is working within.
Below it, "On this page" lists the article's own h2 and h3 headings, and highlights the one currently in view.
Reading order and the pager #
The prev/next links at the foot of an article step through the whole topic in reading order, subfolder contents flattened in place. The last article of one topic does not lead to the first of the next; the pager is a topic-local device.
Topic or book? #
| Use a topic when | Use a book when |
|---|---|
| Articles can be read in any order | The document is read in order, or cited by section |
| No page needs to say "§2.4" | Sections need stable numbers |
| Grouping is for navigation | The whole thing is one document with a cover |
| One level of subfolders is enough | Nesting goes deeper than two levels |
Almost all documentation is topics. Books are for specifications, formal references and manuals — see Books.
Books
Trail / Structuring a site
A book is a document read in order and cited by section: a specification, a formal reference, a manual. Where a topic is a set of related pages, a book is one document that happens to be split across pages.
Books get three things topics do not: a cover page, stable section numbers derived from the directory structure, and appendices.
The directory #
A book is a directory named <order>--<slug>.book, sitting in a product, an anthology or a shelf:
1--pgss.book/ → /demo/pgss (the cover)
├── trail.toml
├── 1--introduction.md §1 → /demo/pgss/introduction
├── 2--logon/ §2 a chapter
│ ├── trail.toml
│ ├── 1--overview.md §2.1 → /demo/pgss/logon/overview
│ └── 2--flow.md §2.2
└── a1--references.md Appendix A
Its trail.toml requires a title and a description:
# 1--pgss.book/trail.toml
= "Peios Generic System Standards"
= "PGSS"
= "Cross-platform system standards from the Peios Project"
| Key | |
|---|---|
title | Required. The full name, shown on the cover and in cards. |
description | Required. The standfirst on the cover, the card text, and the llms.txt entry. |
short | Optional. An abbreviation — usually an acronym — used where the full title will not fit: the sidebar heading, the pill on the book's card, and the line above the title on the cover. |
inline_ref | Optional. Phrases that auto-link to this book, and which may carry a § section suffix in prose. See Inline references. |
Section numbers come from the directory names #
This is the part worth reading twice. A book's section numbers are its order prefixes, joined with dots along the path from the book root:
2--logon/ → §2
1--overview.md → §2.1
3--errors/ → §2.3
1--codes.md → §2.3.1
Two consequences follow:
- Number books from 1, not 100. The hundreds convention used for topic articles would produce §100 and §200. In a book the prefix is the published section number, so
1--,2--,3--is what you want. - Gaps show through, on purpose. If
2--is deleted the book goes §1, §3 — because in a specification, section numbers are addresses. Renumbering to close a gap silently invalidates every citation anyone has ever written; leaving the gap is the honest outcome. Renumber deliberately, or not at all.
Inside an article, h2 and h3 headings continue the numbering as real text: in §2.1, the first h2 is §2.1.1 and its first h3 is §2.1.1.1. Headings deeper than h3 are not numbered. The numbers are rendered into the heading itself rather than drawn with CSS counters, so they can be searched, selected and copied.
Chapters #
A chapter is a plain <order>--<slug> directory — no type suffix, exactly like a topic subfolder — except that chapters may nest as deeply as the document needs.
A chapter:
- contributes a URL segment and a section number;
- has no page of its own — links to it open its first article;
- must contain at least one article, somewhere beneath it. An empty chapter is a build error, because it would appear in the contents as a number leading nowhere;
- takes an optional
trail.tomlwithtitleandinline_ref, with the title otherwise derived from the slug.
Appendices #
An entry whose order is written a<N>-- is an appendix: lettered instead of numbered, and sorted after every numbered sibling at its level.
1--pgss.book/
├── 1--introduction.md §1
├── 2--logon/ §2
├── a1--references.md Appendix A
└── a2--glossary/ Appendix B
└── 1--terms.md §B.1
- Letters run
A,B, …Z,AA,AB, and so on. - The numbering is by
a<N>order, not position:a1--is A,a2--is B. Appendix numbering starts ata1;a0--is an error. - Appendices work at any level, not just the book root. An appendix chapter inside a numbered chapter gives §2.A, and its articles §2.A.1.
- Where the entry itself is displayed — the sidebar, the contents — it is labelled "Appendix A" in full. In section references it is just the letter.
The cover page #
A book's URL is its cover: the product and anthology breadcrumbs, the short name if it has one, the title, the description, and the book's total reading time and last-updated date. The contents are the sidebar tree beside it — the whole book, chapters as collapsible groups, section numbers on every entry.
The tree remembers which chapters you opened, per book, as you move between pages.
Frontmatter is different inside a book #
Book articles use a reduced frontmatter set. title: is still required; description:, related:, inline_ref:, updated: and reading_minutes: all still work.
type: is an error inside a book. The concept/how-to/reference taxonomy describes how to approach a page, and inside a formal document that question is already answered — you read it in order. Trail rejects the key rather than letting it sit there meaning nothing.
Citing sections #
Books are the reason inline references exist. A book that claims a phrase can be cited by section from anywhere in the site:
"PGSS" links to the book; "PGSS §2.1" links to the exact section, resolving through the book's index of section numbers — including numbers that belong to a heading inside an article, not just to the article itself. Within a book's own pages, a bare §2.1 resolves the same way against the surrounding book.
Because a section number is an address, this is where the "gaps show through" rule earns its keep.
When a book is the wrong answer #
A book is a commitment: numbered sections that readers may cite, an order that means something, and a cover page. Most documentation is not that. If your sections would never be cited by number, and the pages can be read in any order, you want a topic.
Link aliases
Trail / Structuring a site
Some pages belong in two places. A "Signing and provenance" article that lives in pekit's Running topic is also the page a reader of the security anthology goes looking for; a specification's terminology section is also an appendix of the manual next to it.
A .link file places an existing page at a second location. The content is not copied — it is the same article, rendered again at a second URL, with its canonical URL still pointing at the original.
Writing one #
A link is a file named <order>--<slug>.link, holding TOML:
# 3--signing.link
= "~pekit/running/signing-and-provenance"
= "Signing and provenance"
| Key | |
|---|---|
target | Required. A ~ page reference, in exactly the grammar body links use. It must start with ~. |
title | Optional. The title the aliased page carries here. Without it, the title is derived from the link's own slug. |
The <order>--<slug> prefix works exactly as it does for an article: the order places the alias among its siblings, and the slug becomes its URL segment. The alias does not inherit the target's slug, its order, or its title — it is a new entry in a new place that happens to render the target's body.
Where a link may live #
.link files sit wherever articles sit: in a topic, in a topic subfolder, in a book, or in a book chapter. They may not sit in a product, an anthology or a shelf.
What each may target:
| In a | May target |
|---|---|
| Topic | an article, or a whole topic subfolder |
| Topic subfolder | an article |
| Book or chapter | an article |
Aliasing a subfolder from a topic clones the entire folder into that topic's URL space — every article in it becomes an alias of its own original, under the new folder's slug. It is the one case where a single link file produces more than one page.
A link may never target another link. That would make the canonical chain ambiguous, so trail rejects it.
What the alias page is #
The aliased page renders exactly like the original: same body, same images, same headings. What differs:
- Its URL and title are the link's, not the target's.
- Its canonical URL is the original's. Search engines are told which of the two URLs is the real one, so the duplicate does not compete with it.
- It is not in the search index. Searching finds the original; the alias exists for people navigating the tree.
- It claims no
inline_refphrases. The original keeps them — a phrase must have exactly one destination. - "Edit this page" points at the original's source file, because that is the file to edit.
- Inside a book, an alias gets a section number from its own order prefix like any other entry, may be an appendix (
a<N>--), and carries notype:ordescription:— book entries never do.
Example #
Pekit's signing article lives in its Running topic. The security anthology wants it too:
pekit.product/3--running.topic/
└── 600--signing-and-provenance.md → /pekit/running/signing-and-provenance
peios.product/1--security-fundamentals.antho/4--supply-chain.topic/
├── 100--overview.md
└── 200--pekit-signing.link → /peios/security-fundamentals/supply-chain/pekit-signing
# 200--pekit-signing.link
= "~pekit/running/signing-and-provenance"
= "Signing in pekit"
Both URLs work. Both show the same prose. Search returns the pekit one. Editing either takes you to the same source file.
When not to use one #
An alias is right when one piece of writing genuinely belongs in two navigational places. It is the wrong tool for:
- A cross-reference. If you just want to point at another page, write a
~linkin the prose, or add it to the article'srelated:list. - A page that should differ between the two locations. An alias is the same article; there is no way to vary it. Write two articles.
- A rename. Aliases are not redirects — the old URL still has to exist as a
.linkfile forever. If a page has moved for good, move it and fix the links; trail will tell you which ones broke.