Book Configuration
docs/_book.yml is the chapter manifest for the PDF book: which pages appear, in what order, and how they group into named parts and chapters. book.mjs reads it during Phase 2 (to resolve page selectors) and Phase 8 (to assemble book.html). See Pipeline Stages for the interface contracts.
- File location and load order
- Top-level structure
- Selector schema
- Pages left out of the book
- Common entry options
- Part-only options
- Heading-shift mechanics
- Sort order
- Worked examples
- See Also
File location and load order
File: docs/_book.yml
data.mjs loads _book.yml during Phase 2 as site.data.book. The orchestrator passes it to resolveBookChapters as site.bookData, which resolves every selector to a concrete Page[] stored as entry._chapters. Phase 8’s assembleBook then needs no further page lookups.
To see the effect of a change, run build.bat then book.bat. build.bat warns about a page the manifest does not mention; see Pages left out of the book.
Top-level structure
front_matter:
- <entry> # zero or more entries, emitted before the first Part
- ...
parts:
- <part> # one or more numbered Parts
- ...
left_out:
- <entry> # pages deliberately not in the book, each with a reason
- ...
front_matter entries are emitted between the title page and the first numbered Part. They produce no divider page and no part number.
parts entries each produce a numbered divider page. A part holds either a flat set of pages or an ordered list of chapters, each with its own sub-divider page.
Front-matter entries, parts and chapters share the selector schema and common entry options.
left_out entries name pages that are not in the book. They use the selector schema and a reason:, and emit nothing; see Pages left out of the book.
Selector schema
An entry may combine any of these keys to select its pages. Matches are contains by default: the page’s URL or nav-path must contain the prefix string. Set no_descent: true on the entry to make all its matches exact equality.
| Key | Type | Description |
|---|---|---|
page | string | Single URL prefix. Shorthand for a one-element pages: list. |
pages | string[] | List of URL prefixes, each tested against the page’s permalink field. |
nav_page | string | Single nav-path prefix. Shorthand for a one-element nav_pages: list. A page’s nav-path is its slash-joined grand_parent / parent / title chain, as populated by nav.mjs. |
nav_pages | string[] | List of nav-path prefixes, tested against each page’s navPath field. |
no_descent | boolean | When true, every match on this entry is exact equality instead of contains. Use it when /Foo/ should match only the index page and not its sub-pages, or when page: / would otherwise sweep in every page on the site. |
An entry with several selector keys collects the union of their selections. A chapter’s selectors are independent of its part’s: the chapter collects its own pages, and the part does not inherit them.
Pages left out of the book
A page that no part, chapter or front_matter entry selects is not in the book. To leave one out, name it in left_out: with the same selector keys and a reason::
left_out:
- reason: Time-limited community contests
page: /Challenges
Every page has to be in one or the other. The build’s summary prints a book: warning for:
- a page that no entry selects and
left_out:does not name; - a page that is in the book and in
left_out:as well; - a book entry that selects no page, or a
left_out:entry that matches none — usually a page that was renamed or deleted; - a
landing_page:orforeword_page:URL that no page publishes at.
A new page with no entry looks like this:
book: 1 page has no entry in _book.yml -- add each to a part, or to left_out with a reason:
IDE/Probe.md (/tB/IDE/Project/Probe)
These are warnings: the exit code does not change. A build with nothing to report prints no book: line, and serve.bat never prints one because it does not build the book.
A link from inside the book to a left-out page opens that page on the website, since a site path goes nowhere in a PDF. The link check’s pass over book.html lists each such link as OUT OF BOOK:
_site-pdf/book.html:
OUT OF BOOK https://docs.twinbasic.com/tB/IDE/Project/PackagePublishing -- not in the book; opens the website
Look there first for a left-out page that belongs in the book after all.
Common entry options
Front-matter entries, parts and chapters all support these options. Where parts and chapters differ, the part form comes first and the chapter form follows in parentheses.
title / subtitle
title: "VBA Package"
subtitle: "Standard runtime modules --- Strings, Math, FileSystem, and the rest"
title is the divider heading — H1 for parts, H2 for chapters. subtitle is an optional subheading below it. Both serve as the PDF bookmark label.
With landing_is_target:, title is injected into the landing page’s article instead of a standalone divider page.
landing_page
landing_page: /tB/Packages/VBA
A single absolute URL. The page is emitted first in the entry’s content, before any prefix-selected pages, and excluded from prefix matches so it appears once. The rewriter strips its source H1, so the divider heading is the entry’s only PDF outline entry. Unlike foreword_page:, it renders with a normal running header and regular article styling.
landing_is_target
landing_page: /tB/Packages/VBA
landing_is_target: true
Requires landing_page:. The divider page renders silently, and the entry title is injected as an H1 (part) or H2 (chapter) at the start of the landing-page article. The PDF bookmark goes to the landing page instead of a blank divider page. The landing’s source H1 is still stripped.
Pair with outline_closed: to start the bookmark collapsed.
no_outline_entry
no_outline_entry: true
Emits the divider title as a silent <p> instead of an H1 or H2. PagedJS skips silent paragraphs when building the PDF outline, so the entry has no bookmark of its own; the first heading in its pages becomes the bookmark target.
Combined with landing_page:, the landing’s source H1 is not stripped, and its first heading becomes the bookmark target.
Pair with no_heading_shift: to keep that heading at the correct depth.
no_heading_shift
no_heading_shift: true
Skips the heading shift that keeps book.html from having several H1s. See Heading-shift mechanics below.
outline_closed
outline_closed: true
Starts the entry’s PDF bookmark collapsed (children hidden until expanded in a PDF reader). The data-pdf-bookmark-closed attribute is stamped on:
- the divider H1 / H2, for entries with a visible divider heading;
- the first content article, for
no_outline_entryentries (PagedJS finds the heading viaclosest()); - the injected heading directly, for
landing_is_targetentries.
Part-only options
foreword_page
foreword_page: /tB/Packages/
A single absolute URL. The page is emitted as <article class="part-foreword"> right after the part divider, before any chapter dividers. CSS suppresses its running header. Unlike landing_page:, its source H1 is kept and it adds no PDF outline entry.
chapters
chapters:
- title: VBA Package
...
- title: VBRUN Package
...
An ordered list of chapter entries. Each chapter has its own divider page (H2) and the same selector schema and common entry options as above. It has no options of its own.
Heading-shift mechanics
The PDF assembler shifts heading levels so that source H1s do not compete with part and chapter divider headings:
- Parts (no chapters): every page in the part is shifted +1 — source H1 renders as H2, H2 as H3, and so on.
no_heading_shift: trueon the part skips the shift and keeps source H1 as H1. - Chapters inside a part: every page is shifted +2 (+1 for the part, +1 for the chapter) — source H1 renders as H3.
no_heading_shift: trueon the chapter skips only the chapter’s +1, so source H1 renders as H2.
Typical pattern: pair no_outline_entry: true with no_heading_shift: true when a single-page part or chapter should use the landing’s own H1 as the bookmark target, with no silent divider above it.
Sort order
Within each entry, sortByNavOrder orders the selected pages:
- Index pages first — any page whose URL ends in
/. - Pages with
nav_order— ascending bynav_order, ties broken bytitle. - Pages without
nav_order— alphabetically bytitle. - Grouped by owning index — an index page and its direct sub-pages stay adjacent.
A landing_page: URL always comes first, before the sorted pages.
Worked examples
Chapter with landing_is_target
- title: VBA Package
subtitle: Standard runtime modules --- Strings, Math, FileSystem, and the rest
landing_page: /tB/Packages/VBA
page: /tB/Modules/
landing_is_target: true
outline_closed: true
In the PDF:
- The chapter divider is silent (no visible H2 page), because of
landing_is_target: true. - The VBA landing page at
/tB/Packages/VBAcomes first, with"VBA Package"injected as an H2 at the top. Its source H1 is stripped. - Every page whose URL contains
/tB/Modules/follows, sorted bysortByNavOrder. - The chapter’s bookmark goes to the VBA landing page and starts collapsed.
Chapter with a visible divider and nav_page selector
- title: Operators
nav_page: Reference Section/Operators
outline_closed: true
In the PDF:
- A visible H2 divider page titled “Operators”.
- All pages whose
navPathcontainsReference Section/Operators, in nav order. - A collapsed bookmark to the divider page.
Front-matter entry with no_outline_entry and no_descent
front_matter:
- title: Introduction
page: /
no_outline_entry: true
no_heading_shift: true
no_descent: true
outline_closed: true
In the PDF:
- The root page (
/) only —no_descent: truestops/from selecting every page on the site. - The divider title “Introduction” is a silent
<p>with no bookmark. The page’s source H1 is the bookmark target instead. - With
no_heading_shift: true, that H1 renders as H1 rather than H2. - The bookmark starts collapsed.
Part with a foreword and nested chapters
- title: Packages
subtitle: The runtime and library packages shipped with twinBASIC
outline_closed: true
foreword_page: /tB/Packages/
chapters:
- title: VBA Package
...
- title: VBRUN Package
...
In the PDF:
- A part divider page (H1) titled “Packages”.
- The page at
/tB/Packages/as a foreword article (no running header, no outline entry), with its H1 kept. - A divider page (H2) for each chapter, followed by that chapter’s pages in nav order.
See Also
- Pipeline Stages – the
book.mjsinterface contracts. - tbdocs Builder – design rationale for
book.mjs.