Templating

Theme layouts are MiniJinja templates. The HTML layouts (layouts/site.html and layouts/document.html) and the paged layout (layouts/pdf.typ) all use the same engine, so the syntax on this page applies to both. The HTML templates and PDF templates pages document the values each layout receives.

Syntax

MiniJinja has two kinds of tags.

Interpolation prints a value:

<title>{{ doc.title }}</title>

Statements control flow. Loops repeat a block:

{% for file in css %}
<style>
{{ file.content }}
</style>
{% endfor %}

Conditionals show a block only when a value is set:

{% if site.logo %}
<img src="{{ site.logo }}" alt="{{ site.logo_alt }}">
{% endif %}

Includes pull in another template file, which is how themes share partials:

{% include "partials/site-footer.html" %}

The MiniJinja syntax reference covers the rest: filters, tests, macros, and more.

Context

Each layout receives a context: a set of named values you reference with {{ }}. The available names depend on the target.

Both targets receive theme, target, and vars.

Variables

Pass project-specific template values with --set vars.<name>=...:

calepin compile notebook.typ --set vars.course="Econ 101" --set vars.semester="Fall 2026"

These values are available as a top-level vars in both HTML and paged layouts. Document-level calepin.setup(vars: ...) values are merged into the same map, and CLI --set vars.<name>=... values take precedence. In HTML templates, vars sits at the top level, not under site:

<p>{{ vars.course }}, {{ vars.semester }}</p>

In a layouts/pdf.typ paged layout, read the same values and emit Typst:

#let course = "{{ vars.course }}"