Templates¶
A template controls presentation. It does not duplicate the source prose. Guidedog uses Jinja for HTML and Typst for PDF.
Templates in Guidedog are standard project files that are directly visible and editable.
When you initialize a project with guidedog quickstart, it writes the actual working
templates directly into your project:
_templates/layout.htmldefines your HTML website structure (Jinja)._templates/book.typdefines your PDF book layout, typography, and cover page (Typst)._static/guidedog.cssand_static/guidedog.jssupply default styles and client behaviors.
Templates are part of your project directory rather than hidden within an external
package or binary cache. You can edit them directly, rebuild with guidedog build,
and preview changes with guidedog serve.
HTML templates¶
Guidedog renders layout.html as the root page template using a built-in Jinja engine.
You are not limited to a single monolithic template file. Projects can break layouts into
modular partials, macro libraries, and multi-tier inheritance trees just like Sphinx
projects.
Modular template structure¶
A project can organize templates across any number of files and subdirectories under
the directories listed in templates_path (by default, ["_templates"]):
my-docs/
├── conf.toml
├── index.rst
└── _templates/
├── layout.html
├── base.html
├── partials/
│ ├── header.html
│ ├── navigation.html
│ ├── searchbox.html
│ └── footer.html
└── macros/
└── components.html
Guidedog scans all files and subdirectories within templates_path. Any file or
partial can be included or imported using paths relative to templates_path:
{% extends "base.html" %}
{% block header %}
{% include "partials/header.html" %}
{% endblock %}
{% block footer %}
{% include "partials/footer.html" %}
{% endblock %}
Jinja macros can also be defined in separate files and imported wherever needed:
{% macro badge(label, type="info") %}
<span class="badge badge-{{ type }}">{{ label }}</span>
{% endmacro %}
{% import "macros/components.html" as ui %}
{{ ui.badge("New", type="success") }}
Theme inheritance with !layout.html¶
If you only want to customize select sections of the default theme rather than rewriting the full page structure from scratch, you can inherit directly from Guidedog’s built-in theme layout using Sphinx’s exclamation mark syntax:
{# Inherit Guidedog's built-in layout #}
{% extends "!layout.html" %}
{# Inject custom metadata or web fonts into the document head #}
{% block extrahead %}
{{ super() }}
<link rel="stylesheet" href="{{ pathto('_static/custom.css', 1) }}">
{% endblock %}
{# Replace or extend the footer with custom content #}
{% block footer %}
{% include "partials/footer.html" %}
{% endblock %}
Using {% extends "!layout.html" %} (or {% extends "basic/layout.html" %}) directs
the loader to fetch the theme’s underlying template instead of recursively loading the project’s
own _templates/layout.html.
Inside any overridden block, calling {{ super() }} renders the parent block’s default
markup, allowing you to prepend or append markup without duplicating existing code.
Layout blocks¶
Guidedog’s built-in layout.html defines blocks matching Sphinx’s conventions:
| Block | Purpose |
|---|---|
doctype |
Document type declaration (default: <!DOCTYPE html>). |
htmltitle |
The <title> element inside <head>. |
linktags |
Navigation and meta link tags (favicon, index, search, prev, next). |
css |
Stylesheet link tags and inline CSS root variables. |
scripts |
JavaScript tags, including search indexing and interactive features. |
extrahead |
Empty insertion point at the end of <head> for analytics, fonts, or custom meta tags. |
header |
Header slot rendered immediately before the main navigation bar. |
relbar1 |
Top navigation bar containing branding, version badge, search box, and theme toggle. |
rootrellink |
Insertion point in navigation bar before related links. |
relbaritems |
Custom items inserted into the navigation bar. |
sidebar1 |
Left-hand navigation sidebar container. |
sidebartoc |
Table of contents navigation tree rendered inside sidebar1. |
breadcrumbs |
Hierarchical breadcrumb navigation path above the article content. |
document |
Article wrapper enclosing the page body. |
body |
Rendered HTML content of the current document ({{ body }}). |
relbar2 |
Bottom pagination controls offering previous and next chapter links. |
footer |
Page footer containing copyright notice, last updated timestamp, and source links. |
sidebar2 |
Right-hand secondary rail containing the local page outline (“On this page”). |
Finding templates¶
Guidedog searches each directory listed in conf.toml under templates_path in order,
followed by its built-in templates. Template names are relative to the directories in
templates_path and cannot escape outside of them using parent directory traversal (..).
Every file and subdirectory in templates_path is registered as an input dependency of the
build. Whenever any template or partial file is added, edited, or removed, guidedog build
and the live server guidedog serve automatically detect the change and re-render the site.
Page variables¶
Where Sphinx has a name for a variable, Guidedog uses it, so parts of Sphinx themes carry over.
| Variable | Value |
|---|---|
body |
The document, as HTML. |
title |
The document’s title. |
pagename, docname |
The document’s name, such as usage/install. On a page that is no
document’s, docname is empty and pagename is the page’s name, as in
Sphinx: genindex, py-modindex, search, or the name of a page made
from a template. |
toc |
The navigation built from the toctrees, as HTML. |
outline |
The document’s own sections, as HTML; empty when it has none. |
prev, next |
The neighbouring pages in reading order, with url (also link) and
title; none at either end. |
project, version, release, copyright, language |
The settings of the same names. |
html_title, docstitle |
html_title, by default “<project> <release> documentation”. |
html_short_title, shorttitle |
html_short_title. |
root_doc, master_doc |
The root document’s name. |
pathto_root |
The path from the page to the site’s root, such as ../. |
root_url, search_url, genindex_url |
Links to the root page, the search page, and the general index. |
css_files, js_files |
Lists of files with a url: Guidedog’s own, then html_css_files and
html_js_files. |
logo_url, favicon_url |
html_logo and html_favicon below _static; empty when unset. |
accent |
html_theme_options.accent, the theme’s colour. |
sourcelink_url |
The document’s source below _sources, when it is shown; otherwise empty. |
last_updated |
The build date in html_last_updated_fmt when it is set; otherwise empty. |
show_copyright, show_sphinx, show_guidedog, has_source,
show_source |
The html_show_* and html_copy_source settings. |
builder, file_suffix |
The builder’s name, such as html, and the page suffix. |
every key of html_context |
Its value, with its type, as Sphinx passes a Python value: false is false
in {% if %}, numbers are numbers, arrays are lists, and tables are dicts
whose keys keep the order conf.toml gives them. guidedog migrate
carries the literal entries of conf.py’s html_context; a value
conf.py computes is left undefined, which a template reads as false. |
pathto is Sphinx’s template function. pathto("usage/install") is the URL of a
document’s page, and pathto("_static/logo.svg", 1) the URL of a file below the
site’s root, both relative to the current page. With the singlehtml builder, as with
Sphinx’s, a document is its section of the single page: pathto("usage/install") is
#document-usage-install on that page and index.html#document-usage-install on the
index and search pages. A name that is no document’s, such as genindex or a page
made from a template (below), is a page at the site’s root with every builder.
Pages made from templates¶
html_additional_pages makes pages from a template alone, as in Sphinx. Each key is
a page’s name and each value a template in templates_path:
root_doc = "contents"
[html_additional_pages]
index = "indexcontent.html"
download = "download.html"
A page’s template usually extends layout.html and fills its blocks, so the page
looks like every other one:
{% extends "layout.html" %}
{% block htmltitle %}<title>{{ shorttitle }}</title>{% endblock %}
{% block body %}
<h1>{{ docstitle|e }}</h1>
<p><a href="{{ pathto("tutorial/index") }}">Tutorial</a></p>
{% endblock %}
The template sees the variables of a page that is no document’s: pagename is the
page’s name (index), title and body are empty, and pathto, toc,
toctree(), and the values of html_context work as on any page. The page is
written at the site’s root as <name>.html (html_file_suffix) by html,
dirhtml, and singlehtml, and written again on every build; a change to its
template, as to any file in templates_path, rebuilds every page.
Where the page goes, and where it differs from Sphinx:
- A page named as a document takes that document’s place, as in Sphinx, where it is
written last:
index = "landing.html"makes the landing pageindex.htmlwhileindex.rststill gives the table of contents its entries. Withsinglehtmlthe single page keeps its place and the template page is not written, with a warning. dirhtmlwrites the page asdownload.html, besidegenindex.htmlandsearch.html, where Sphinx writesdownload/index.html;pathto("download")gives that file.- A name must be a file name at the site’s root:
"sub/page"is refused with a warning (build.additional_page), since the page’s links are made from the root. So is a name whose file the site already has, such asgenindex, where Sphinx would write one of the two over the other. guidedog migratecarrieshtml_additional_pagesover fromconf.py.
Errors¶
A mistake in a template stops the build with the template’s file and line, the line itself, and a suggestion:
TEMPLATE ERROR template.error
_templates/base.html:1
No filter named 'defualt'.
Did you mean the filter 'default'?
A variable that is not defined renders as nothing, as in Jinja.
Runaway templates stop with an error too, never a crash: syntax nested more than 100
levels deep (brackets, tags, and each link of a long chain of operators, filters or
elif branches), macros, includes and recursive loops more than 200 deep, or any
recursion needing more than 512 KiB of stack. A list that contains itself prints as
[...], as in Python.
PDF templates¶
The PDF book is typeset by Typst. _templates/book.typ is an ordinary Typst file
that defines a book function; Guidedog writes the book’s source as
#import "/_templates/book.typ": book
#show: book.with(title: ..., author: ..., version: ..., date: ...,
lang: ..., paper: ..., numbering: ..., logo: ...)
// the chapters, one per document of the root toctree
so everything that decides how the book looks is in that file: fonts, page size and margins, headings, the title page, running heads, and the table of contents.
Parameters¶
| Parameter | Value |
|---|---|
title |
The book’s title in pdf_documents, otherwise project. |
author |
The book’s author, otherwise author. |
version |
release. |
date |
|today|: today when it is set, otherwise the build date in today_fmt. |
lang |
language. |
paper |
"a4", or "us-letter" when pdf_paper_size is letter. |
numbering |
true when a toctree has the numbered option. |
logo |
The path of pdf_logo; absent when it is unset. |
copyright |
copyright, for the colophon; passed only to a template that takes it. |
body |
The chapters. |
A template may add parameters with defaults of its own. The template quickstart
writes has accent, the fonts serif, sans, and mono, the text size,
and page-ref, which follows cross-references to other pages with their page
number when set to a function such as n => [p. #n].
Modular Typst templates¶
Like HTML templates, Typst templates are not restricted to a single file. You can break
book styling, macros, and custom covers into multiple modular .typ files under
_templates/ and compose them using standard Typst #import and #include statements:
#import "cover.typ": title-page
#import "typography.typ": apply-styles
#let book(
title: "",
author: "",
version: "",
date: "",
lang: "en",
paper: "a4",
numbering: false,
logo: none,
body,
) = {
apply-styles()
title-page(title: title, author: author, version: version, logo: logo)
body
}
What a template can use¶
- Typst packages
-
#import "@preview/cetz:0.4.2"and other packages work, downloaded on first use. Setpdf_packages = "offline"to build only with packages already on disk. - Fonts
-
The system’s fonts are available, as with the
typstcommand. Add folders withpdf_font_paths, or setpdf_fonts = "embedded"to use only the fonts built into Typst, for byte-for-byte reproducible books. - A preamble
-
pdf_preamblenames a Typst file whose content follows the#show: bookline, which suits a fewsetandshowrules without a template of your own. - Typst in a document
-
Documents are reStructuredText and Markdown; Typst files are not documents. To put Typst markup into the book, write it in a raw block, which web pages leave out:
.. raw:: typst #align(center)[#text(size: 14pt)[Only in the book]]
.. only:: pdfkeeps ordinary content to the book in the same way.
When Typst reports an error, the build names the file and line, whether it is in your
template or in the generated book. Each PDF has its own generated source at
_build/pdf/sources/<pdf-filename>.typ. For example, reference-en.pdf has
sources/reference-en.pdf.typ. A single-book build also keeps the familiar
_build/pdf/book-<language>.typ inspection copy.
A failed build publishes nothing. Its source remains in _build/.doctrees/failed
at the path named by the error. Several books keep distinct failure sources.
Writing a new template¶
Start from the copies quickstart wrote, which show every part in use:
- Keep the
layout.htmlmarkup that the default style sheet expects, or replace_static/guidedog.cssalong with it. - Move repeated parts into files of their own and
includethem, or put a base layout inbase.htmlandextendsit fromlayout.html. - For the book, change the
bookfunction’s rules, or write a new function with the same parameters and keep its namebook.
Preview with guidedog serve while editing. Save the source and refresh the browser;
the server rebuilds changed inputs before serving the page.