folderweb/docs/04-development/06-templates.md
Ruben b03511f99b Update AGENT.md and add architecture documentation
Update AGENT.md to reflect current project structure and philosophy

Add comprehensive architecture documentation covering:
- Directory layout
- Stable contracts
- Request flow
- Module dependencies
- Page vs list detection
- Deployment models
- Demo fallback

Remove outdated plugin system documentation
Add new content system documentation
Add configuration documentation
Add context API documentation
Add hooks and plugins documentation
Add templates documentation
Add rendering documentation
Add development environment documentation
2026-02-05 23:30:44 +01:00

4.8 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.

  1. Check custom/templates/{name}.php
  2. 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):

  1. custom/templates/{page_template}.php
  2. app/default/templates/{page_template}.php
  3. 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
$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)
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

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: $content is 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.