Documentation

Tag: tera

Writing and customizing templates

Writing and customizing templates Everlock uses [Tera](https://keats.github.io/tera/) as its template engine. Tera's syntax is similar to Jinja2 and Django templates. Templates live in the layouts/ directory of your site repository. This page explains the syntax, the context variables available in each template, and common customization patterns. Template syntax basics Output a variable <h1>{{ page.title }}</h1> Double braces output the value of a variable. Any HTML in the value is escaped by default. Output raw HTML safely <div class="content"> {{ page.content | safe }} </div> The | safe filter tells Tera the value is already trusted HTML and should not be escaped. Use this for rendered Markdown content. Conditionals {% if page.git_date %} <p class="meta">Updated {{ page.git_date }}</p> {% endif %} {% if page.tags %} <ul> {% for tag in page.tags %} <li><a href="/tags/{{ tag }}">{{ tag }}</a></li> {% endfor %} </ul> {% endif %} Loops {% for link in site.nav.links %} <a href="{{ link.url }}">{{ link.label }}</a> {% endfor %} Default values <time>{{ p.git_date | default(value=p.date) | default(value="") }}</time> Filters chain left to right. | default(value=x) substitutes x when the left side is empty or undefined. String filters {{ page.title | upper }} {{ tag | replace(from="-", to=" ") | title }} Common Tera filters: upper, lower, title, trim, replace, truncate, length, first, last. Date helpers Everlock adds a small date layer on top of Tera for Markdown sites: <p>Now: {{ now() }}</p> <p>Today: {{ today() }}</p> {% if page.date is before(date=now()) %} <p>This page date is in the past.</p> {% endif %} <time>{{ page.date | date_format(format="%Y-%m-%d") }}</time> Accepted input formats: - YYYY-MM-DD - RFC3339 timestamps such as 2026-06-11T08:30:00Z today() and date-only comparisons use the site timezone from site.toml. If no timezone is set, Everlock falls back to UTC. Filtering lists The where filter keeps the items of a list whose attribute matches a value — plain equality for scalar attributes, containment for list attributes such as tags: {% set git = all_pages | where(attribute="tags", value="git") %} {% set updates = all_pages | where(attribute="section", value="/updates") %} Dotted paths reach nested fields, so custom frontmatter is filterable too: {% set news = all_pages | where(attribute="extra.kind", value="news") %} Items missing the attribute are dropped. where composes with the other list filters (sort, reverse, first) and with paginate() below. Pagination A list layout controls its own pagination with the paginate() function. It slices the listing's full item set by the requested page (?page=N) and returns the current page plus a navigation model: {% set pager = paginate(by=50) %} {% for p in pager.items %} <a href="{{ p.url }}">{{ p.title }}</a> {% endfor %} {% if pager.pagination.total_pages > 1 %} <nav> {% if pager.pagination.has_prev %}<a href="{{ pager.pagination.prev_url }}">Previous</a>{% endif %} <span>{{ pager.pagination.current_page }} / {{ pager.pagination.total_pages }}</span> {% if pager.pagination.has_next %}<a href="{{ pager.pagination.next_url }}">Next</a>{% endif %} </nav> {% endif %} Called with only by=, paginate() works on the listing's complete newest-first item set — the same collection that feeds pages — so the layout's page size wins over the site-wide paginate_by. An items= argument paginates a list the template assembled itself, which composes with filtering and sorting: {% set pager = paginate(items=my_filtered_list, by=10) %} by=0 turns paging off: pager.items holds every item and pager.pagination is null. The function is available in section listings, tag pages, and taxonomy pages. Templates that render the prepared pages variable keep the site-wide paginate_by behavior unchanged. Independent paginators on one page A param= argument names the query parameter a paginator tracks (default page). Combined with where, one layout paginates several filtered lists independently — each list's Previous/Next links change only their own parameter and keep the rest of the query string, so navigating one section leaves the others where they are: <h2>Git articles</h2> {% set git = all_pages | where(attribute="tags", value="git") %} {% set g = paginate(items=git, by=10, param="git_page") %} {% for p in g.items %}<a href="{{ p.url }}">{{ p.title }}</a>{% endfor %} {% if g.pagination.total_pages > 1 %} {% if g.pagination.has_next %}<a href="{{ g.pagination.next_url }}">More</a>{% endif %} {% endif %} <h2>UI articles</h2> {% set ui = all_pages | where(attribute="tags", value="ui") %} {% set u = paginate(items=ui, by=10, param="ui_page") %} {% for p in u.items %}<a href="{{ p.url }}">{{ p.title }}</a>{% endfor %} {% if u.pagination.total_pages > 1 %} {% if u.pagination.has_next %}<a href="{{ u.pagination.next_url }}">More</a>{% endif %} {% endif %} Requesting ?git_page=2 shows page 2 of the git list while the UI list stays on page 1; the git section's "More" link becomes ?git_page=3 with any other parameters preserved. --- Template inheritance Every template in Everlock extends base.html. The pattern uses {% extends %} and {% block %}: layouts/base.html defines named blocks: <!doctype html> <html> <head> <title>{% block title %}{{ page.title }} | {{ site.title }}{% endblock %}</title> </head> <body> <nav> {% for link in site.nav.links %} <a href="{{ link.url }}">{{ link.label }}</a> {% endfor %} </nav> <main> {% block main %}{% endblock %} </main> </body> </html> layouts/single.html fills in the block: {% extends "base.html" %} {% block main %} <article> <h1>{{ page.title }}</h1> <div class="content">{{ page.content | safe }}</div> </article> {% endblock %} Any block not overridden in the child template renders from the parent. A child can call {{ super() }} to include the parent block's content and then add to it. --- Context variables reference Available in all templates | Variable | Type | Content | |---|---|---| | site.title | string | Site title from site.toml | | site.description | string | Site description from site.toml | | site.base_url | string | Base URL from site.toml | | site.timezone | string | Optional site timezone used by today() and date formatting | | site.nav.links | list | Navigation links, each with .label and .url | | data | map | All .toml files from the data/ directory | In single.html (content pages) | Variable | Type | Content | |---|---|---| | page.title | string | Title from frontmatter | | page.content | string | Rendered HTML of the page body | | page.summary | string | HTML of content before