Skip to content

Templates ​

Templates are Twig files in templates/. In production they're compiled to PHP classes in var/cache/twig (by deploy or on first use).

The layout ​

templates/_layout.twig is the page shell. Two parts of it matter to TinyTank:

twig
<html lang="{{ site.language }}">
<head>
    {{ theme_script() }}            {# first: light/dark before anything is painted #}
    {% block seo %}{% endblock %}   {# pages can set metadata here… #}
    {{ seo_tags() }}                {# …printed once: title, description, Open Graph, hreflang, JSON-LD #}
    {{ public_config() }}           {# config values allowlisted for JavaScript #}
    {{ vite('resources/js/app.js', page_scripts ?? []) }}   {# plus the page's own bundles #}
</head>

A page sets its metadata either in its controller ($this->app->seo->title(…)) or in its template, in the seo block that renders before the tags are printed:

twig
{% extends '_layout.twig' %}
{% block seo %}{% do seo.title('About').description('Who we are.') %}{% endblock %}

What TinyTank adds to Twig ​

NameKindExample
path(name, params, language)function{{ path('blog_post', {slug: post.slug}) }}
tfilter{{ 'Load more'|t }}, {{ '{n} posts'|t({n: 3}) }}
t(message, params)function{{ t('Blog') }}
vite(entry, …)function{{ vite('resources/js/app.js', page_scripts ?? []) }}
public_config()functionconfig values allowlisted for JavaScript
vite_preload(source, …)function<link rel="preload"> for a built font or image
theme_script()functionthe light/dark script for the top of <head>
content(item)functiona post, page or item's HTML with its components rendered: {{ content(post) }}
posts(), pages(), collection(name)functionscontent queries: posts().tag('php').limit(3).all() (Querying content)
seo_tags()functionprints the page's metadata
absolute_url(path)function{{ absolute_url('/blog') }} → https://example.com/blog
language_switcher()functionthe current page in every language
format_date, format_number…filters{{ post.date|format_date('long', locale: site.locale) }} (Twig Intl)
siteglobalsite.name, site.language, site.locale
seoglobal{% do seo.title('…') %}
datastarglobaldatastar.get('_partials/search') (Datastar)

Plus the Datastar tags patch_elements, patch_signals, remove_elements, execute_script and location used in partials. The Twig reference has every signature.

Partials ​

Files in templates/_partials/ are fragments: included by pages, and rendered on their own by Datastar requests. The leading underscore is a convention; Datastar can render any template.

Error pages ​

templates/_error.twig renders every error (404, 405, 500) with two variables, status and message. Error pages get noindex automatically.

Escaping ​

Twig escapes everything by default. Use |raw only for HTML you trust; the blog's rendered post HTML is safe to print raw, because Markdown is parsed with raw HTML escaped and unsafe links removed.

Debug mode ​

With APP_DEBUG=1, templates aren't cached and strict_variables is on: using an undefined variable is an error instead of an empty string. Use ?? for optional variables: {{ signals.q ?? '' }}.

TinyTank documentation