Every helper on this page reads one object: this.documentationConfiguration, which is present only on pages bound to a documentation page type. Without it they all take the same branch — a single documentationConfiguration was not located! line at info level, and a return of undefined or an empty string. Nothing throws, nothing warns, and the page still renders. That is why a documentation page that comes out as a bare article is almost always a binding problem rather than a template problem: the page type is not a documentation page type, or its configuration was never filled in.
Everything here assumes the mechanics in How Helpers Work; the Helper Index (A–Z) is the way back out if you arrived by name.
Layout and theme: docsLayoutName, docsColorTheme, docsLayoutTemplate
The first two return configured strings and nothing more. The third turns the layout name into a partial path — leed/docs/<layoutName>/base — which is what makes a single documentation layout able to render three different shells:
{{#if (docsLayoutName)}}
{{> (docsLayoutTemplate) }}
<!-- Generated by Leed AI - '{{ docsLayoutName }}' layout using '{{ docsColorTheme }}' theme -->
{{else}}
<h1>documentationConfiguration is missing!</h1>
{{/if}}That is the shipped documentation layout in full, and the {{else}} branch is worth memorizing: a page rendering the literal heading documentationConfiguration is missing! is the symptom of an unconfigured documentation page type, not of a broken template. It is the one place in the docs stack where the missing-configuration case is visible rather than silent.
Three layouts ship — alpha, bravo and charlie — and layoutName is a fixed choice among them rather than a free string: what each one looks like, and why a fourth is not something a plan change can buy, is at Documentation Layouts.
These three helpers are the most internal on the page. You call them when you are writing your own documentation layout; a customer overriding only the header or the footer never touches them.
leedConfiguredCSS — the theme class string
No parameters. It joins the configured font, docs-layout-<layoutName>, colorTheme and codeTheme into one space-separated string, appending each only when it is set, and returns an empty string when there is no configuration at all. The shell puts the result on the root element:
<html lang="en" class="scroll-smooth js-focus-visible leed-documentation {{ leedConfiguredCSS }}">Everything that themes a documentation set hangs off that one attribute, which makes the helper easy to reason about and easy to be misled by.
Writing the utility that gives a custom theme name meaning — and the rest of the emission rules — is at Custom Documentation Themes.
docsMenuName — from slot to menu name
One parameter, one of top, left or bottom. It returns the menu’s name key, not its id:
{{> leed/menu/builder menuName=(docsMenuName "left") }}The indirection is the whole point, and it is worth spelling out because it is the most likely misconfiguration in a hand-edited documentation configuration. The three slots each store a menuId. The top-level keys of the menu data are menu names. docsMenuName scans the menus for the one whose id matches the slot and hands back its key — which is what the menu partial takes, and the reason that partial asks for a name rather than an id.
One asymmetry is useful to know while you are here: only the left slot classifies a menu as a documentation menu. That is the test the CMS uses to decide whether a menu gets folder-path derivation, whether folder items have their hrefs blanked, and whether a page item may have children. A menu you already use as a site header or footer can therefore be pointed at top or bottom without acquiring any of that behavior. The left slot is the one that changes what a menu is — see Left Navigation Menu. The partner partial’s own parameters are at Menu Partials.
Previous and next: previousPageNav, nextPageNav
Both take one positional label — the small word under the link — and both return raw HTML, so both need triple braces:
<nav id="docs-pagination" class="mt-8">
{{{ previousPageNav (default prevLabel "") }}}
{{{ nextPageNav (default nextLabel "") }}}
</nav>default there is a handlebars-helpers helper, not a Leed one; the composition exists so a layout can pass a label or not and the helper always receives a string.
The mechanism: the left menu is flattened depth-first into an ordered list of every item that has an href, the current page is located in that list by matching either its URL or pageid: plus its page id, and the adjacent item is returned. So the reading order is exactly your left-nav order, and reordering the nav reorders the reading path — including across folder boundaries, because the flattening ignores nesting.
The emitted markup puts the page title in the label span and the word you passed in the sublabel span:
<a class="docs-pagination__link docs-pagination__link--prev" href="pageid:d506879a-a181-4fac-94c7-c1157db68d52">
<span class="docs-pagination__label">Menu-Safety Helpers</span>
<span class="docs-pagination__sublabel"><i class="docs-pagination__icon--prev"></i>Previous</span>
</a>Pass an empty label and the arrangement changes rather than degrading: the icon moves onto the page title in the label span, and the sublabel is emitted empty. The icon leads on prev and trails on next in both arrangements.
Passing nothing is a third case, and not a good one. There is no parameter shift here, so the trailing options object lands in the label slot, tests as truthy, and is interpolated into the markup — you get the string [object Object] where the word “Previous” belongs. That is why the shipped partial routes an unset label through default instead of leaving the argument off.
Both helpers return undefined — nothing at all in the output — in three cases: there is no documentation configuration, the left slot is empty or unresolvable, or the current page is not in that menu. The last is the common one, and it is the answer to “why does this page have no next link”: a page that is not a leaf of the left menu has no neighbors to find.
Note that the href above is still a pageid:. Prev/next links are not filtered against what actually published; they are rewritten later, along with every other pageid: link on the page. What that means for a partly published set is covered at Menu-Safety Helpers.
breadcrumbsNav
One optional parameter carrying the label — or the markup — for the home crumb. Raw HTML, so triple braces again:
<nav class="leed-breadcrumbs mb-6">
{{{ breadcrumbsNav '<i class="home"></i>' }}}
</nav>The home crumb is emitted only when both halves are present: you passed something, and documentationConfiguration.startingPage is set. Pass nothing and the trail starts at the first real ancestor.
The path rule is the surprising part. The helper finds the current page in the left menu and keeps the page itself plus only those ancestors that have no href — folder-style parents. A parent that is itself a link is skipped:
Documentation (folder, no href) → becomes a crumb
└─ Template Helpers (folder, no href) → becomes a crumb
└─ Overview (leaf, has an href) → skipped if it were an ancestor
└─ this page → always the last crumbIn a documentation left nav that rule is invisible, because folders never carry an href — the CMS blanks it on every save. It becomes visible the moment you point breadcrumbsNav at a menu you built by hand, where a linked section header simply will not appear in the trail.
The emitted markup is an ordered list with an <i> between crumbs:
<ol class="leed-breadcrumbs__crumbs has-separators">
<li class="leed-breadcrumbs__crumb"><a href="/docs/" class="is-index" aria-current="false"><i class="home"></i></a><i class="leed-breadcrumbs__separator"></i></li>
<li class="leed-breadcrumbs__crumb"><span aria-current="false">Template Helpers</span><i class="leed-breadcrumbs__separator"></i></li>
<li class="leed-breadcrumbs__crumb"><a href="pageid:8284f745-b10c-4b18-86f3-74782ac225ec" aria-current="location">Documentation Navigation Helpers</a></li>
</ol>Crumbs with an href become anchors, crumbs without become spans, the home crumb carries is-index, and the last crumb is the only one marked aria-current="location" — the rest carry aria-current="false" explicitly rather than omitting the attribute. The helper returns an empty string when there is nothing to show, and undefined when the left menu id is missing entirely.
toc — the on-this-page list
toc is not a Leed helper. It comes from eleventy-plugin-toc, added with no global options, and it reaches your templates because Eleventy filters are registered onto the Handlebars instance alongside the shortcodes. It takes the page’s rendered content and a JSON options string, and returns a nested list of links to the headings it finds:
<aside id="leed-toc" class="py-6 px-4">
<p id="leed-toc-heading"><i class="fa-regular fa-list-ul"></i> On this page</p>
{{{ toc content '{"tags": ["h2", "h3"], "wrapper": "div", "wrapperClass": "leed-nav-toc"}' }}}
</aside>That is the shipped call: two heading levels rather than the default three, a <div> wrapper rather than a <nav>, and the class the documentation stylesheet hangs the rail’s geometry off. The options string is JSON, and a string that fails to parse is discarded silently in favor of the defaults — so a stray trailing comma costs you the settings without telling you.
| Option | Type | Default | Effect |
|---|---|---|---|
tags | array of strings | ["h2", "h3", "h4"] | Which heading levels are collected, in nesting order |
wrapper | string | "nav" | The element wrapped around the list. A falsy value returns the bare list |
wrapperClass | string | "toc" | The class on that wrapper |
wrapperLabel | string | unset | Adds aria-label to the wrapper; omitted when unset |
ul | boolean | false | false renders <ol>; true renders <ul> |
flat | boolean | false | true emits every heading at one level instead of nesting children |
Two behaviors cause most toc questions:
- Headings without an
idare skipped. The filter selects on the attribute, so an unanchored heading is not merely unlinked — it is absent from the list, and any headings nested under it are reparented. - It returns
undefined, not an empty string, when nothing qualifies. A page with no matching headings renders no list at all, which is correct, but means an empty rail is indistinguishable from a broken one. If the “On this page” column is blank, count theh2s before suspecting the filter.
Where heading ids come from
Content written in the CMS is anchored for you: headings from h1 through h5 get a slugified id automatically as the page is rendered. A sixth-level heading gets none, and so never appears in a table of contents — one of several reasons to stop at five. How those anchors are slugified, and what that means for linking to a section, is at Basic Formatting.
Headings you write by hand in a layout or a partial are not touched by that pass. If you want them in the table of contents, give them an id yourself.
Reference
| Helper | Form | Parameters | Reads from documentationConfiguration | Returns | Braces | When config is missing |
|---|---|---|---|---|---|---|
docsLayoutName | inline | none | layoutName | the configured layout name | {{ }} | undefined, logs documentationConfiguration was not located! |
docsColorTheme | inline | none | colorTheme | the configured theme name | {{ }} | undefined, same log line |
docsLayoutTemplate | inline | none | layoutName | leed/docs/<layoutName>/base | used as {{> (docsLayoutTemplate) }} | undefined, same log line; also undefined when layoutName is unset |
leedConfiguredCSS | inline | none | font, layoutName, colorTheme, codeTheme | a space-separated class string | {{ }} | "", same log line |
docsMenuName | inline | menu — top, left or bottom, required | menus[<slot>] | the menu’s name key | {{ }} or as a subexpression | undefined, logs documentationConfiguration.menus…was not located! |
previousPageNav | inline | label string, required — pass "" for none | menus.left | raw HTML anchor, or undefined | {{{ }}} | undefined; also when the page is not in the left menu |
nextPageNav | inline | label string, required — pass "" for none | menus.left | raw HTML anchor, or undefined | {{{ }}} | undefined; also when the page is not in the left menu |
breadcrumbsNav | inline | homeName string or markup, optional | menus.left, startingPage | raw HTML <ol>, "", or undefined | {{{ }}} | undefined when the left id is missing; "" when it resolves to no menu |
toc | inline — external filter, not a Leed helper | content, required · options JSON string, optional | — | raw HTML list, or undefined | {{{ }}} | not applicable — it reads content, not configuration |
Every field these helpers read is enumerated, with its default and where you set it, at Documentation Configuration Reference. What the output looks like from a reader’s side of the screen is described at Documentation Reading Experience. If you are replacing the shell rather than reading it, the partials your override has to cooperate with are cataloged at Documentation Shell Partials, and the narrower job of swapping only the header or footer is at Customizing the Documentation Header and Footer.