Concepts

Frontmatter, mounts, bundles, preload, and hydration — how the build decides where everything goes.

Frontmatter

YAML at the top of a .c.html file between ---. System keys (all caps) control the build:

  • MOUNT — Which slot receives this file's body (e.g. content, nav).
  • TYPEpage or component.
  • TEMPLATE — Which .t.html wraps the page (pages only).

Any other keys (e.g. title, heroTitle) are yours. They are available as {{key}} in the template and when passing into mounts (e.g. {{MOUNT.hero:heroTitle}}).

Frontmatter example
---
title: Home
heroTitle: Welcome
TYPE: page
MOUNT: content
TEMPLATE: layout.t.html
---

Mounts

Named slots where content goes. On <style> and <script> you set mount="head" or mount="body" (or any name). In the template you have {{MOUNT.head}}, {{MOUNT.content}}, {{MOUNT.body}}, and any other slot names you use.

The body HTML of a page or component goes into the slot specified by MOUNT: in frontmatter. The build merges everything that targets a given slot into one place. Duplicate style or script blocks are not emitted twice.

In .c.html: target a slot
<style mount="head" serve="inline">.x{}</style>
<script mount="body" serve="file" bundle="main" destination="static/js"></script>
In template: place the slot
<head>
  {{MOUNT.head}}
</head>
<body>
  {{MOUNT.content}}
  {{MOUNT.body}}
</body>

Passing variables into a mount

{{MOUNT.hero}} inserts the hero slot's HTML. {{MOUNT.hero:heroTitle}} does the same but passes a variable: the component can use @@heroTitle or {{heroTitle}} and {{if heroTitle}}{{/if}}. For several values use {{MOUNT.card:title,description}}; in the component use @@title, @@description, and conditionals on those names.

Page: pass one variable
{{MOUNT.hero:heroTitle}}
Component: use @@var and conditionals
{{if heroTitle}}
  <h1>@@heroTitle</h1>
{{else}}
  <h1>Default</h1>
{{/if}}

Bundles

With serve="inline" (the default) each <style> or <script> is emitted inline in place. With serve="file" and a bundle="main" (or any name), all blocks that share that bundle name are merged into one file. You only need one destination per bundle (e.g. destination="static/js"). If no block in the bundle specifies it, the file is written at the dist root and the build warns. Duplicate blocks are not emitted; each block is included once.

Different bundle names (e.g. bundle="components") produce separate files. Same rules for styles.

Inline (default) vs file bundle
<style mount="head" serve="inline">.a{}</style>
<script mount="body" serve="file" bundle="main" destination="static/js">console.log(1);</script>

Preload

Without preload_mount you get normal output only: <link rel="stylesheet" href="..."> for file CSS (in the mount) and <script src="..."> in body for file JS—both can block rendering. To avoid blocking, add preload so the build emits a fetch in the head first.

Styles: On <style mount="head" serve="file" ...> add preload_mount="head". The build emits <link rel="preload" href="..." as="style"> in that mount (plus the normal stylesheet link). Omit preload_mount and you only get <link rel="stylesheet" href="...">.

Scripts: On <script> with serve="file" and type="module" add preload and preload_mount="head". The build emits <link rel="modulepreload" href="..."> in that mount and the script tag in body. Only pages that use that bundle get the link.

<style mount="head"
           serve="file"
           bundle="main"
           destination="static/css"
           preload_mount="head">...</style>

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

Hydration

Use hydrate="load", hydrate="idle", hydrate="visible", or hydrate="visible:#id" to control when the script runs. If you omit the attribute, the script runs immediately. See Examples for snippets.

<script mount="body" serve="inline" hydrate="idle">/* runs when idle */</script>
<script mount="body" serve="inline" hydrate="visible:#panel">/* runs when #panel in view */</script>