Table of Contents

Content helpers

The syntax Baseline adds to your Markdown, and the front-matter key that keeps a page out of a build: drafts, wikilinks, automatic heading ids, attributes on any element, and images.

All of it is on the moment the plugin loads and none of it takes an option. Each one changes what you write rather than what you configure, which is why they sit together here rather than on a module page.


Drafts

Any page with draft: true in front matter is dropped on build and kept on serve. The preprocessor is on automatically; you don't register it. Use it for in-progress pages you want to preview locally without shipping.

---
title: 'Half-written thoughts'
draft: true
---

Forward links, MediaWiki-style. All resolved at render time, all combinable:

  • [[slug]] links to the page with that slug. The link text defaults to the page's title.
  • [[slug#anchor]] links to a heading on that page. The anchor is slugified for you.
  • [[slug:lang]] hops to the translation in the named language. Use the short code that keys settings.languages, e.g. [[about:fr]], not a full BCP 47 tag.
  • [[slug|alias]] sets custom link text. The pipe is the alias separator.

The forms compose. [[about:fr#team|notre equipe]] is valid. Misses (unknown slug, missing translation) render as the original literal text rather than a broken link, so a typo is visible without crashing the build.

Slugs are global within the default language. Wikilinks themselves are forward-only; the content-graph pre-pass does build a reverse index, exposed as data._backlinks per page and _navigator.backlinks site-wide, so authors can opt in to "what links here" surfaces without the wikilink syntax sprouting them automatically.

Curated cross-references still win over automatic ones in narrative prose.


Where they work

Wikilinks are a markdown-it inline rule, so they resolve in .md body content only. They do not run in Nunjucks templates or in front matter values. If you need a link from a layout or a data field, write the URL directly.


Formatters will eat them

Wikilinks are not standard markdown, so a formatter has no reason to preserve them. Prettier rewraps and reflows markdown, and an alias can be lost in the process: [[commercial-support | commercial support]] comes back as [[commercial-support]].

Nothing warns you. A wikilink that lost its alias still resolves, so the build stays green and the link text silently changes to the page title. One that gets broken outright renders as literal text, which is the design, but on a page you were not looking at.

If you run format-on-save, exclude markdown. In .prettierignore:

# Wikilinks are not standard markdown and Prettier rewrites them
**/*.md

Heading IDs

Every heading in your Markdown gets a stable id attribute automatically. The id is the slugified heading text, so ## Implementation details becomes <h2 id="implementation-details">. You can link to it directly with #implementation-details, and _node.headings carries the same id so a generated table of contents matches the anchors it points at.

Repeated headings dedup with WordPress-style numeric suffixes: the first stays foo, the second becomes foo-2, the third foo-3, and so on. Stable across builds for the same source order.


Overriding an id

A manual id wins over the auto one. Use the attribute syntax described below:

## Implementation details {#impl}

Renders as <h2 id="impl">. Manual ids are also seeded into the dedup map, so a later heading with the same slugified text gets the next free name (implementation-details here, since impl does not collide).


Why one source

Heading ids live in two places that need to agree: the rendered HTML you anchor links to, and the headings[].id field on the content graph that templates read for a TOC. They are assigned once in the markdown engine; the rendered HTML and the graph read the same id back. No drift between "the link" and "the table of contents entry".


Element attributes

The {#id .class key="value"} syntax attaches attributes to any block element. It runs through the upstream markdown-it-attrs plugin, which Baseline ships as a direct dependency.

## A section heading {#impl .lead}

A paragraph with a class. {.note}

![Alt text](/img/hero.jpg){#hero loading="eager"}

The attributes attach to the element that ended just before them. Useful for adding a class to a paragraph, anchoring a heading with a specific id, or marking an image so a transform can find it later.

Loaded under safeUse, so if you already wired markdown-it-attrs yourself with different options, your registration wins and Baseline's is skipped silently.

The same dedup applies to the heading-id and wikilinks plugins; you can layer your own markdown extensions without fighting Baseline's wiring.


Page breaks

A <!--pagebreak--> in the body splits the page into parts, one Eleventy page each. You write the marker and nothing else: no pagination block, no permalink, no template.

Part one keeps whatever URL the page already had, and the rest are numbered underneath it:

/docs/how-to/integrate/        part one
/docs/how-to/integrate/2/      part two
/docs/how-to/integrate/3/      part three

The grammar

The same shape as a wikilink, so if you know [[slug#anchor|Label]] you already know this:

<!--pagebreak-->                     split; the part is numbered
<!--pagebreak|Data files-->          split; the part is named "Data files"
<!--pagebreak#setup-->               split; the part's entry point is #setup
<!--pagebreak#setup|Data files-->    both

A label names the part that follows the marker, not the act of moving forward, so "Next page" is a poor label and "Data files" is a good one. Without a label the part is numbered, which needs no copy and no translation.

Where a marker is ignored

Inside a fenced block, an indented block, or inline code. The scan walks markdown-it tokens rather than lines, so a marker in any of those is a fence, code_block or code_inline and never reaches the splitter. That is what lets this page document the syntax without splitting itself.

A Nunjucks raw block is not a shelter. The preprocessor runs before Nunjucks, so at that point a raw block is ordinary text: a marker inside one still splits the page, and leaves each half with an unbalanced tag, which fails the build. Put the marker in a fence, which needs no raw block anyway.

When Baseline declines to split

Three cases, each one leaving the page whole rather than splitting it badly:

  • The page already paginates. Two pagination configs cannot share a template, and yours is the one you asked for.
  • The page sets eleventyComputed.permalink. Computed data resolves after plain data and wins, so the numbering would be discarded and every part would collide on one URL.
  • The page sets permalink: false. A page that is never written has nothing to split.

A permalink function is fine, and is the common case. Baseline calls it and numbers the parts under whatever it returns.

What the parts share

Every part inherits the page's front matter, so every part carries the same title, description and slug. A wikilink to that slug lands on part one, and only part one joins a collection.

Repeated titles and descriptions across the parts are fine. Google does not ask paginated pages for distinct ones, and each part canonicalises to itself.

Rendering the navigation

Splitting a page gives you the parts; drawing the links between them is yours. A split page carries _pagebreak, which holds the part numbers, their labels and their URLs. It is undefined everywhere else, so one {% if _pagebreak %} in a layout covers a whole site. See Globals for the shape.


Images

The shortcode is Baseline's. The heavier transform isn't.

  • {% image %} is always registered. eleventy-img is a peer dependency, so it sits in your package.json rather than inside the plugin; install it alongside Baseline.
  • AVIF, WebP and JPEG by default. The first two are what save the bytes; the third is there because the <img> fallback has to be readable by whatever fell back to it.
  • The HTML transform that rewrites <img> into responsive <picture> (eleventyImageTransformPlugin) is opt-in. You add it yourself if you want it. When it is present, the shortcode marks its own output with eleventy:ignore to avoid double-processing.

Alt text matters. The shortcode throws when it is missing, so the build stops rather than shipping the page; use an empty string for a decorative image, which is a different statement from having none. The image transform how-to walks you through the responsive pipeline.


See also