Templating Guide
Accent CMS uses MiniJinja for templating, providing Jinja2-style syntax with template inheritance, filters, functions, and control flow. Templates live in your theme’s templates/ directory and have access to page content, site configuration, and a rich set of helpers.
Template Basics
Templates use the standard Jinja2 syntax:
- Variables:
{{ variable }}outputs a value - Tags:
{% tag %}executes logic (if/for/block/extends) - Comments:
{# comment #}are stripped from output
Template inheritance lets you define a base layout and override blocks in child templates:
{# base.html.jinja #}
<html>
<body>
{% block content %}{% endblock %}
</body>
</html>
{# default.html.jinja #}
{% extends "base.html.jinja" %}
{% block content %}
<h1>{{ page.title }}</h1>
{{ page.content | safe }}
{% endblock %}
Topics
This guide is organized into focused sub-pages. Each covers a specific aspect of Accent CMS templating:
Template Syntax
The base MiniJinja language Accent runs - delimiters, variables and expressions, control flow, assignments, template inheritance, macros, whitespace control, and auto-escaping - plus the exact engine version and features Accent ships and the built-in filter/test lists. Start here if you are new to Jinja-style templating.
Filters and Functions
Custom filters (date, truncate, slugify) and functions (now(), media(), url(), debug()) for transforming data and generating dynamic content. Also covers the dev context and accent build information.
Page Context
Page hierarchy (parent, children, siblings, breadcrumbs, prev/next), table of contents, computed metadata (word count, reading time), taxonomy/tags, page metadata properties, and auto-excerpt generation.
Collections and Pagination
Content collections via frontmatter, filtering and sorting, automatic pagination with page navigation, static build output, and named content relationships between pages.
Media in Templates
Page media assets (page.media), type filters (filter_images, filter_videos, filter_documents), image processing with responsive_image() and thumbnail(), video embedding with video() and video_embed().
Modular Pages
Composing a single page from multiple _-prefixed subdirectories, each with its own frontmatter, content, and custom fields. Ideal for landing pages and feature showcases.
Structured Sections
Defining multiple named content regions within a single markdown file using ---section: name--- markers. A lightweight alternative to modular pages for simple multi-region layouts.
Social Sharing and SEO Metadata
Open Graph, Twitter Cards, build-safe canonical links, and the open meta map. The absolute_url() helper, the resolved page.description/page.social_image/page.meta_tags primitives, and how to add any platform tag from config without touching a template.
Custom Frontmatter Fields
Any YAML key in the frontmatter that is not a built-in field (title, date, author, lead, tags, published, template, menu, relations) is passed through to templates via page.custom.
Markdown frontmatter:
--- title: My Product hero_image: /images/hero.jpg featured: true social: twitter: "@myhandle" github: "myrepo" ---
Template usage:
{# Access custom scalar fields #}
<img src="{{ page.custom.hero_image | safe }}" alt="{{ page.title }}">
{% if page.custom.featured %}
<span class="badge">Featured</span>
{% endif %}
{# Access nested custom fields #}
<a href="https://twitter.com/{{ page.custom.social.twitter }}">Twitter</a>
{# Check if a custom field exists #}
{% if page.custom.hero_image is defined %}
<div class="hero" style="background-image: url('{{ page.custom.hero_image | safe }}')"></div>
{% endif %}
Custom fields support strings, numbers, booleans, lists, and nested objects.
Combining Filters and Functions
Filters and functions can be combined:
{# Truncated debug output #}
{{ debug(page) | truncate(200) }}
{# Slugified title for anchor #}
<a name="{{ page.title | slugify }}"></a>
Built-in MiniJinja Features
In addition to the custom filters and functions above, MiniJinja provides:
Built-in Filters:
safe- Mark HTML as safe (no escaping)escape/e- HTML escapelower/upper- Case conversiontrim- Remove whitespacelength- Get collection lengthfirst/last- Get first/last itemjoin- Join list with separatordefault- Provide fallback valuesort/reverse- List ordering
Built-in Tests:
defined/undefinednoneodd/eveneq/ne/lt/le/gt/ge
Control Flow:
{% if %}/{% elif %}/{% else %}{% for item in list %}{% include "partial.html.jinja" %}{% extends "base.html.jinja" %}/{% block name %}
Quick Reference
| Type | Syntax | Example |
|---|---|---|
| Filter | {{ value | filter }} | {{ title | slugify }} |
| Filter with arg | {{ value | filter(arg) }} | {{ text | truncate(50) }} |
| Function | {{ function() }} | {{ now() }} |
| Function with arg | {{ function(arg) }} | {{ debug(page) }} |
| Property access | {{ object.property }} | {{ now().year }} |
| Chained filters | {{ value | f1 | f2 }} | {{ title | lower | slugify }} |
Markdown Templates
When the Markdown View feature is enabled with default_format: markdown, Accent CMS resolves *.md.jinja templates instead of *.html.jinja. These templates output structured markdown using the same template engine, variables, and inheritance system as HTML templates.
Template Resolution
HTML mode: {page.template}.html.jinja -> default.html.jinja (fallback)
Markdown mode: {page.template}.md.jinja -> default.md.jinja (fallback)
If no .md.jinja template exists at all, the raw page content is served without template wrapping.
Template Variables
In markdown mode, page.content contains the raw markdown body (not rendered HTML). All other template variables remain unchanged:
| Variable | Markdown Mode | HTML Mode |
|---|---|---|
page.content | Raw markdown | Rendered HTML |
page.title, page.url, etc. | Same | Same |
page.children, page.toc | Same | Same |
page.custom.* | Same | Same |
In HTML mode with Markdown View enabled, two additional variables are available:
| Variable | Type | Description |
|---|---|---|
page.markdown_url | string | URL to view the raw markdown (e.g., /docs/guide.md) |
markdown_view_enabled | bool | Whether the feature is enabled site-wide |
Example Markdown Template
{# default.md.jinja #}
{% extends "base.md.jinja" %}
{% block content %}
# {{ page.title }}
{% if page.date %}*{{ page.date }}*{% endif %}
{{ page.content }}
{% if page.tags %}
**Tags:** {% for tag in page.tags %}[{{ tag }}](/tags/{{ tag }}){% if not loop.last %}, {% endif %}{% endfor %}
{% endif %}
{% endblock %}
“View as Markdown” Link
In HTML mode, include the partial to show a link to the raw markdown source:
{% include "partials/markdown-link.html.jinja" %}
The partial renders a link to page.markdown_url only when markdown_view_enabled is true.
Custom Error Pages
Accent CMS supports themed error pages through the template system. When an error occurs (e.g., a 404 Not Found), Accent CMS resolves the error page through this chain:
- Content page:
content/error/404.md(orcontent/error/404.{lang}.mdfor i18n) - Specific template:
error/404.html.jinja - Generic template:
error.html.jinja - Built-in: Plain text or dev error page (if no templates exist)
Error Context
Error templates receive an error context object with these fields:
| Field | Type | Description |
|---|---|---|
error.status | number | HTTP status code (e.g., 404, 500) |
error.title | string | Human-readable title (e.g., “Page Not Found”) |
error.message | string | Error description |
error.path | string | The requested URL path |
error.language | string | Detected language code |
Example Error Template
{% extends "base.html.jinja" %}
{% block content %}
<div class="error-page">
<h1>{{ error.status }}</h1>
<h2>{{ error.title }}</h2>
{% if page.content %}
{{ page.content | safe }}
{% else %}
<p>The page <code>{{ error.path }}</code> could not be found.</p>
{% endif %}
<a href="/">Back to home</a>
</div>
{% endblock %}
Error Content Pages
Create a content page at content/error/404.md to provide custom body text:
--- title: Page Not Found template: error/404 menu: visible: false --- The page you are looking for does not exist or has been moved.
The rendered markdown appears as page.content in the error template. Set menu.visible: false to keep error pages out of navigation.
Print and PDF Output
The default theme ships a print stylesheet at
themes/default/assets/css/print.css, linked from base.html.jinja with
media="print":
<link rel="stylesheet" media="print"
href="{{ cdn_url('/' ~ (('theme/assets/css/print.css') | fingerprint)) }}">
Because it is behind media="print", the browser fetches it at low priority
and applies it only when the page is printed or saved as a PDF. It can never
affect the screen render.
The sheet is deliberately not listed under assets.css in theme.yaml – that
list produces screen <link> tags. Keep the print sheet as its own <link>
so the media attribute survives.
What it does
| Area | Behaviour in print |
|---|---|
| Chrome | Hides the header and nav, the sidebar and “On this page” rail, the search box and overlay, prev/next and pagination navigation, the theme toggle, version and language switchers, and the dev debug panel |
| Colour | Redefines the design tokens to black-on-white, including dark mode, and drops background fills so a page does not flood the printer with toner |
| Layout | Expands the article to the full print width once the side rails are gone, including the .prose reading-measure cap the utility layer applies (65ch is right for a wide screen, but on A4 it leaves roughly a third of the sheet empty) |
| Code | Wraps long lines instead of clipping them, and flattens syntax-highlight colours to black – highlight themes are usually dark, and their pale glyphs would print near-invisible |
| Fragmentation | Keeps headings with the text that follows, and keeps code blocks, tables, quotes, figures, and admonitions whole when they fit on a page |
| Links | Appends the destination of external links, so [the repo](https://example.com) prints as “the repo (https://example.com)”. In-page anchors and image links are left alone |
A block taller than a single page still breaks across pages – that is correct. The flip side of keeping a large table whole is that it moves to the next sheet when it does not fit in the space left, which can leave a gap at the bottom of the previous one.
Overriding page size and margins
Page geometry lives in the @page rule. A4 with 18mm/16mm margins is the
default; override it in your own theme’s stylesheet to switch to US Letter or
different margins:
@page { size: letter; margin: 0.75in 0.6in; } @page :first { margin-top: 1in; }
To suppress the printed URL after a specific link – an icon or image link, say
– give it the no-print-url class:
<a href="https://example.com" class="no-print-url">Docs</a>
Running headers, generated page numbers, and a page-numbered table of contents
are not part of this: browsers do not honour @page margin boxes or
counter(page) without a JavaScript paginator, so declaring them would have no
effect.
Example: Dynamic Footer
Here’s a complete example showing filters and functions in a footer:
<footer>
<p>© 2025-{{ now().year }} {{ site.name }}</p>
<p>Page: {{ page.title | slugify }}</p>
<p>Tags: {% for tag in page.tags %}{{ tag }}{% if not loop.last %}, {% endif %}{% endfor %}</p>
</footer>
In this section
-
Template Syntax
The base MiniJinja language Accent runs - variables, expressions, control flow, inheritance, macros, whitespace control, and auto-escaping - plus the exact engine version and features Accent ships.
-
Filters and Functions
Transform data with filters and generate dynamic content with functions like now(), media(), recent_documents(), and debug().
-
Page Context
Access page hierarchy, breadcrumbs, siblings, taxonomy, computed metadata, and auto-excerpts in templates.
-
Collections and Pagination
Define content collections with filtering and sorting, paginate results, and create named page relationships.
-
Diagrams in Templates
Render Mermaid (and other) diagrams from Jinja templates and frontmatter using the diagram() template function.
-
Media in Templates
Work with page media assets, image processing, responsive images, thumbnails, and video embedding in templates.
-
Modular Pages
Compose a single page from multiple content sections using modular subdirectories with independent frontmatter.
-
Structured Sections
Define multiple named content regions within a single markdown file for multi-region page layouts.
-
Social Sharing and SEO Metadata
Open Graph, Twitter Cards, build-safe canonical links, and the open meta map -- every page shareable by default with zero per-page authoring.