Update documentation for new JS variables and rendering pipeline Add script.js file support to content directories Implement cache-busting for JavaScript files Update static file serving to include JavaScript files Document page-specific script loading in base.php
5.1 KiB
Templates
Template Hierarchy
Three levels, rendered inside-out:
Content (Markdown/HTML/PHP rendered to HTML string)
→ page.php or list-*.php (wraps content in semantic markup)
→ base.php (HTML scaffold: doctype, head, header, nav, main, footer)
Template Resolution (canonical reference)
resolveTemplate(string $name): string — in helpers.php. This is the single resolution function used throughout the framework.
- Check
custom/templates/{name}.php - Fall back to
app/default/templates/{name}.php
The three core templates (base, page, list) are resolved at context creation and stored in $ctx->templates. Other docs reference this section for resolution behavior.
List Template Override
In metadata.ini:
[settings]
page_template = "list-grid"
Resolution for list template override (in router.php, at render time):
custom/templates/{page_template}.phpapp/default/templates/{page_template}.php- Fall back to
$ctx->templates->list(the default resolved at context creation)
Default Templates Provided
| Template | Purpose |
|---|---|
base.php |
HTML document scaffold |
page.php |
Single page wrapper (<article><?= $content ?></article>) |
list.php |
Default list with cover images, dates, summaries |
list-compact.php |
Minimal list variant |
list-grid.php |
Card grid layout |
Template Variables
Variables are injected via extract() — each array key becomes a local variable.
base.php Variables
| Variable | Type | Always Set | Description |
|---|---|---|---|
$content |
string (HTML) | yes | Rendered output from page.php or list template |
$pageTitle |
?string | yes | Page title (null if unset) |
$metaDescription |
?string | no | SEO description |
$socialImageUrl |
?string | no | Cover image URL for og:image |
$navigation |
array | yes | Menu items: ['title','url','order'] |
$homeLabel |
string | yes | Home link text |
$pageCssUrl |
?string | no | Page-specific CSS URL |
$pageCssHash |
?string | no | CSS cache-bust hash |
$pageJsUrl |
?string | no | Page-specific JS URL |
$pageJsHash |
?string | no | JS cache-bust hash |
$feedUrl |
?string | no | Atom feed URL (only on lists with feed = true) |
$currentLang |
string | plugin | Language code (from languages plugin) |
$langPrefix |
string | plugin | URL language prefix |
$languageUrls |
array | plugin | [lang => url] for language switcher |
$translations |
array | plugin | UI strings for current language |
page.php Variables
All base.php variables plus:
| Variable | Type | Description |
|---|---|---|
$content |
string (HTML) | Rendered page content |
$metadata |
?array | Page metadata from metadata.ini |
list-*.php Variables
All base.php variables plus:
| Variable | Type | Description |
|---|---|---|
$items |
array | List items (see schema below) |
$pageContent |
?string (HTML) | Rendered content from the list directory itself |
$metadata |
?array | List directory metadata |
List Item Schema
Each entry in $items:
| Key | Type | Always | Description |
|---|---|---|---|
title |
string | yes | From metadata, first heading, or folder name |
url |
string | yes | Full URL path with trailing slash and lang prefix |
date |
?string | no | Formatted date string (plugin-processed) |
rawDate |
?string | no | ISO YYYY-MM-DD date (for feeds, <time> elements) |
summary |
?string | no | From metadata |
cover |
?string | no | URL to cover image |
pdf |
?string | no | URL to first PDF file |
redirect |
?string | no | External redirect URL |
dirPath |
string | yes | Filesystem path to item directory (internal use) |
Items sorted by date — direction controlled by order metadata on parent (descending default, ascending available).
Asset URLs vs page URLs: Item url uses the translated slug. Asset paths (cover, pdf) use the actual folder name, ensuring assets resolve regardless of language.
Adding Custom Template Variables
Via Hook::TEMPLATE_VARS:
Hooks::add(Hook::TEMPLATE_VARS, function(array $vars, Context $ctx): array {
$vars['myVar'] = 'value';
return $vars;
});
Then in any template: <?= $myVar ?>.
Template Conventions
- Escape all user-derived output:
<?= htmlspecialchars($var) ?> - Exception:
$contentis pre-rendered HTML — output raw:<?= $content ?> - Check optional vars:
<?php if (!empty($var)): ?> - Use null coalescing for defaults:
<?= $var ?? 'fallback' ?> - Use short echo tags:
<?= $expr ?> - Semantic HTML5:
<article>,<nav>,<header>,<footer>,<time>,<main> - ARIA labels on navigation elements
- Classless defaults — the default theme uses semantic HTML without classes where possible
Partials
Not a framework feature — just PHP includes. Convention:
custom/templates/partials/post-card.php
Include from templates:
<?php foreach ($items as $post): ?>
<?php include __DIR__ . '/partials/post-card.php'; ?>
<?php endforeach; ?>
Variables from the parent scope are available in the included file.