Examples

Copy-paste patterns: bundles, mounts, conditionals, loops, reusability, and build control. In code blocks, @@variables {{variables}} and {{MOUNT}} are green; everything else is white.

Bundles: inline vs file

Keep styles inline and send scripts to a file. Every block with the same bundle="main" (and serve="file") is merged into one output file. You only need one destination per bundle; other blocks in that bundle can omit it. Different bundle names (e.g. bundle="components") produce separate files.

Page (e.g. index.c.html)
---
TYPE: page
MOUNT: content
TEMPLATE: layout.t.html
---
<style mount="head" serve="inline">
  .box { padding: 1rem; border: 1px solid #333; }
</style>

<div class="box">Content</div>

<script mount="body" serve="file" bundle="main" destination="static/js">
  console.log('bundled');
</script>

MOUNT with a variable

The page passes a value in frontmatter; the component receives it and can show a fallback when the value is missing. Use the same variable name in both: heroTitle in frontmatter. The component’s HTML is inserted where you write {{MOUNT.hero:heroTitle}}. In the component use @@heroTitle and {{if heroTitle}}.

Page
---
title: Home
heroTitle: Welcome
TYPE: page
MOUNT: content
TEMPLATE: layout.t.html
---
{{MOUNT.hero:heroTitle}}

<div>Rest of page...</div>
Component (hero.c.html)
---
TYPE: component
MOUNT: hero
---
<div class="hero">
  {{if heroTitle}}
    <h1>@@heroTitle</h1>
  {{else}}
    <h1>Default title</h1>
  {{/if}}
</div>

Multiple values into a mount

Pass several variables as a comma-separated list. In the component use @@title, @@description, @@image, and conditionals like {{if title}}. Useful for cards, list items, or any reusable block that needs different data per use.

{{MOUNT.card:title,description,image}}

In the card component: @@title, @@description, @@image, and {{if image}}<img src="@@image">{{/if}}.

Schema (JSON-LD)

Emit <script type="application/ld+json"> from a component. The template mounts the schema with whatever variables you need{{MOUNT.schema:var1,var2,...}}—and the component uses @@var1, @@var2, etc. in the JSON. Below is one example: Article. You could have other components or the same component with different @type (e.g. WebPage, Organization, Product) and pass whatever fields that type needs.

Component (schema.c.html) — example: Article
---
TYPE: component
MOUNT: schema
---
<script type="application/ld+json" mount="schema" serve="inline">
{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "@@name",
  "description": "@@description",
  "url": "@@url",
  "datePublished": "@@datePublished",
  "dateModified": "@@dateModified",
  "author": {
    "@type": "Person",
    "name": "@@author"
  }
}
</script>
Page frontmatter (e.g. index.c.html)
---
title: Page Title
name: Page title
description: My Description
url: https://example.com/
datePublished: 2026-03-07
dateModified: 2026-03-07
author: Author Name
TYPE: page
MOUNT: content
TEMPLATE: index.t.html
---
Template — mount with whatever vars this schema type needs
  {{MOUNT.schema:name,description,url,datePublished,dateModified,author}}

For this Article example we pass those six. For WebPage you might pass name,description,url; for another type, whatever that type needs. Page frontmatter supplies the values; the component’s JSON uses @@name, @@description, etc.

Conditionals

Use {{if variable}}{{else}}{{/if}} for optional blocks. Nested conditionals work (evaluate inside-out). The variable can come from frontmatter or from a mount argument.

{{if showSidebar}}
  <aside class="sidebar">...</aside>
{{else}}
  <p>No sidebar</p>
{{/if}}

Foreach (lists)

Define a list in frontmatter as items: '["a","b","c"]' or items: Apple, Banana, Cherry. In the body use {{foreach items}}; inside the loop use {{index}} (0-based) and {{item}}. For arrays of objects use {{item.name}}, {{item.url}}.

Frontmatter
items: '["a","b","c"]'
Body
<ul>
{{foreach items}}
  <li>{{index}}: {{item}}</li>
{{/foreach}}
</ul>

Preload + module script

For scripts with serve="file" and type="module", add preload and preload_mount="head". The build emits <link rel="modulepreload" href="..."> in the head and the script tag in body. Only pages that actually use this bundle get the preload link.

<script mount="body"
        serve="file"
        bundle="main"
        destination="static/js"
        type="module"
        preload
        preload_mount="head">
  // your module code
</script>

Hydration: idle, load, or visible

Control when inline scripts run so they don't block the main thread. hydrate="idle" runs when the browser is idle; hydrate="load" runs on window.load; hydrate="visible" or hydrate="visible:#my-section" runs when the element (or page) enters the viewport. Omit the attribute to run immediately.

<script mount="body" serve="inline" hydrate="idle">
  // runs when thread is free
</script>

<script mount="body" serve="inline" hydrate="load">
  // runs on window load
</script>

<script mount="body" serve="inline" hydrate="visible:#my-section">
  // runs when #my-section enters viewport
</script>

Reusability

One template per content type: e.g. docs.t.html for all docs pages, blog.t.html for posts. File-based routing: src/docs/concepts.c.html/docs/concepts.html. Reuse styles and markup as components—mount a shared nav, footer, or a dynamic hero that takes a page title so every page gets a consistent hero with @@pageTitle. No duplication; change the component once, every page updates.

Template (e.g. docs.t.html) — one layout for all docs
<!DOCTYPE html>
<html lang="en">
<head>
  <title>{{title}}</title>
  {{MOUNT.head}}
</head>
<body>
  {{MOUNT.nav}}
  <main>
    {{MOUNT.content}}
  </main>
  {{MOUNT.footer}}
  {{MOUNT.body}}
</body>
</html>
Every docs page: pass title into the hero
---
title: Concepts
TYPE: page
MOUNT: content
TEMPLATE: _templates/docs.t.html
---
{{MOUNT.pagehero:title}}

<article>...</article>
Component (pagehero.c.html) — one hero, any page title
---
TYPE: component
MOUNT: pagehero
---
<header class="page-hero">
  {{if title}}
    <h1>@@title</h1>
  {{/if}}
</header>

Build control & page speed

Critical CSS inline so first paint isn't blocked. Non-critical CSS and JS in files would render-block if you just dropped <link rel="stylesheet"> and <script src="..."> in the critical path. The fix: put rel="preload" (and for modules rel="modulepreload") in the head so the browser fetches them without blocking the critical path—async in the header, then the real link/script applies or runs when ready.

Source: critical inline; non-critical in bundles; script preload so build can emit preload in head
<!-- Critical: inline so no extra request blocks first paint -->
<style mount="head" serve="inline">
  .hero { min-height: 50vh; }
  .content { max-width: 60ch; }
</style>

<!-- Non-critical: one file, minified; preload_mount="head" = build emits rel="preload" in head -->
<style mount="head" serve="file" bundle="main" destination="static/css" preload_mount="head">
  .nav { ... }
  .footer { ... }
</style>

<script mount="body" serve="file" bundle="main" destination="static/js"
        type="module" preload preload_mount="head">
  // build emits <link rel="modulepreload"> in head = non-blocking
</script>
Built output: critical inline; preload links in head (non-blocking)
<head>
  <meta charset="UTF-8">
  <title>...</title>
  <style>.hero{min-height:50vh}.content{max-width:60ch}</style>
  <link rel="preload" href="static/css/main.css" as="style">
  <link rel="modulepreload" href="static/js/main.js">
</head>
<body>
  ...
  <script src="static/js/main.js" type="module"></script>
</body>

If you don't pass preload_mount, you get the normal output only: <link rel="stylesheet" href="..."> for CSS and <script src="..."> in body for JS (both can be render-blocking). With preload_mount="head" on <style>, the build also emits <link rel="preload" href="..." as="style"> in the head. With preload and preload_mount="head" on <script>, the build also emits rel="modulepreload" in the head so the critical path stays free.

Nav with current page flag

Define the nav links once in a component and pass a simple nav_item value from each page. The component compares nav_item to hard-coded ids and sets aria-current="page" on the active link.

Component (nav.c.html)
---
TYPE: component
MOUNT: nav
---
<style mount="head_scripts" serve="inline">
  .nav { border-bottom: var(--border); padding: 0 var(--gap); }
  .nav-list { list-style: none; display: grid; grid-template-columns: repeat(2, auto); gap: var(--gap); margin: 0; padding: 1rem 0; }
  .nav-list a { display: block; padding: 0.75rem 0; }
  .nav-list a { color: var(--fg); text-decoration: none; text-transform: uppercase; font-size: 0.85rem; letter-spacing: 0.1em; }
  .nav-list a:hover { color: var(--accent); }
  .nav-list a[aria-current="page"] { color: var(--accent); font-weight: 700; }
</style>

<nav class="nav" aria-label="Main">
  <ul class="nav-list">
    <li><a href="/coupled-docs/" {{if nav_item == "index"}}aria-current="page"{{/if}}>Home</a></li>
    <li><a href="/coupled-docs/about/" {{if nav_item == "about"}}aria-current="page"{{/if}}>About</a></li>
  </ul>
</nav>
Template or page — mount nav with current item
{{MOUNT.nav:nav_item}}
Page frontmatter
---
title: Home
nav_item: index
TYPE: page
MOUNT: content
TEMPLATE: index.t.html
---