Templating #
Glaze uses Sugar for page rendering – a PHP template engine with inheritance, blocks, components, and directives.
Templates live in templates/ and use the .sugar.php extension. Sugar automatically escapes output based on context (HTML, attribute, URL, JS, CSS), so you do not need a manual htmlspecialchars() call for most values.
This page covers Glaze-specific integration. For the full directive and language reference, see the Sugar documentation.
Template inheritance #
Sugar uses s:extends and s:block for layout inheritance. A base layout defines named blocks; child templates fill them in.
templates/layout/base.sugar.php:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title s:block="title"><?= $site->title ?></title>
</head>
<body>
<main s:block="content"></main>
</body>
</html>
templates/page.sugar.php:
<s-template s:extends="layout/base">
<title s:prepend="title"><?= $page->title ?> | </title>
<s-template s:block="content">
<article>
<h1><?= $page->title ?></h1>
<?= $content |> raw() ?>
</article>
</s-template>
The |> raw() pipe disables escaping for the rendered $content HTML. All other output is escaped automatically by Sugar.
For full inheritance options (s:prepend, s:append, s:block nesting), see the Sugar inheritance docs.
Per-page template override #
Override the template for a single page in frontmatter:
---
title: Landing page
template: landing
---
This renders the page with templates/landing.sugar.php instead of the configured default.
Available variables #
These variables are injected into every page template by Glaze:
| Variable | Type | Description |
|---|---|---|
$title |
string |
Page title |
$content |
string |
Rendered Djot HTML |
$url |
string |
Current URL path |
$page |
ContentPage |
Full page object |
$meta |
array |
Merged page metadata |
$site |
SiteConfig |
Global site config |
$page properties #
$page->title // string
$page->urlPath // string -- e.g. /blog/my-post/
$page->slug // string -- e.g. blog/my-post
$page->relativePath // string -- e.g. blog/my-post.dj
$page->source // string -- raw Djot source
$page->draft // bool
$page->type // ?string -- resolved content type name
$page->meta // array<string, mixed> -- all frontmatter metadata (keys lowercased)
$page->taxonomies // array<string, array<string>>
$page->toc // list<TocEntry> -- TOC entries collected during rendering (empty until rendered)
$page->meta('hero.title') // dotted access to nested frontmatter
$page->meta('hero.primaryAction.href', '/') // dotted access with default fallback
$page->hasMeta('hero.highlights') // existence check
Metadata helper methods #
ContentPage provides dotted metadata helpers for nested frontmatter values:
-
$page->meta(string $path, mixed $default = null): mixed- Returns the metadata value at the dotted path. - Returns$defaultwhen the path does not exist. - Returns the full metadata map when$pathis empty. -
$page->hasMeta(string $path): bool- Returnstruewhen metadata exists at the dotted path. - Returns whether metadata is non-empty when$pathis empty.
$site properties #
$site->title // ?string
$site->description // ?string
$site->baseUrl // ?string
$site->basePath // ?string -- e.g. /blog for subfolder deploys
$site->meta('hero.title') // dotted access to custom site.* config keys
$site->siteMeta('hero.title') // alias for readability
$site->hasSiteMeta('hero.primaryAction') // existence check
Site context ($this) #
Inside every template, $this is a SiteContext instance with helpers for querying all site content:
$this->pages() // all pages
$this->regularPages() // pages without a content type
$this->tree() // root Section node
$this->section('blog') // ?Section for a path (supports nesting: docs/guides)
$this->type('blog') // pages with content type 'blog'
$this->taxonomyTerm('tags', 'php') // pages tagged 'php'
$this->isCurrent('/blog/') // bool
$this->assets(?string $subdirectory = null) // ContentAssetCollection from content root
$this->pageAssets(?string $subdirectory = null) // ContentAssetCollection for current page
$this->assetsFor($page, ?string $subdirectory = null) // ContentAssetCollection for arbitrary page
$this->sections() // array<string, Section> — top-level sections, ordered by weight
$this->rootPages() // PageCollection — root-level pages (no section)
$this->previous(?callable $predicate = null) // ?ContentPage — previous page in global display order
$this->next(?callable $predicate = null) // ?ContentPage — next page in global display order
$this->previousInSection(?callable $predicate = null) // ?ContentPage
$this->nextInSection(?callable $predicate = null) // ?ContentPage
$this->extension('name') // call a project extension (see Extensions)
Content assets in templates #
Use content asset helpers to discover non-Djot files from content directories:
$this->pageAssets() // assets next to current page source
$this->pageAssets('gallery') // assets in current page subfolder
$this->assets('shared') // assets from content/shared/
$this->assetsFor($item, 'gallery') // assets for a page in a loop
$this->section('blog')?->assets() // direct assets in content/blog/
$this->section('blog')?->allAssets() // recursive assets in content/blog/**
When iterating a page collection, use $this->assetsFor($item, ...):
<ul>
<li s:foreach="$this->section('blog')?->pages() ?? [] as $item">
<h3><?= $item->title ?></h3>
<img
s:if="!$this->assetsFor($item, 'gallery')->images()->isEmpty()"
src="<?= $this->assetsFor($item, 'gallery')->images()->first()?->urlPath ?>"
alt="<?= $item->title ?>"
>
</li>
</ul>
Each item is a ContentAsset with fields like relativePath, urlPath, filename,
extension, and size, plus helper checks:
$asset->is('png', 'jpg')
$asset->isImage()
See Page collections for filtering, sorting, grouping, and pagination. For custom PHP logic callable from templates, see Extensions.
Sugar directives #
Sugar provides attribute-based directives for control flow, looping, and conditional class merging. A brief example:
<!-- loop -->
<li s:foreach="$this->section('blog')?->pages()->take(5) ?? [] as $post">
<a href="<?= $post->urlPath ?>"><?= $post->title ?></a>
</li>
<!-- conditional -->
<span s:if="$page->draft">Draft</span>
<span s:else>Published</span>
<!-- conditional class -->
<a href="<?= $post->urlPath ?>" s:class="['active' => $this->isCurrent($post->urlPath)]">
<?= $post->title ?>
</a>
For the complete directive reference – including s:unless, s:forelse, s:switch, s:isset, loop metadata ($loop->first, $loop->last), components, and the pipe syntax – see the Sugar documentation.
Vite template integration (s:vite) #
When Vite is enabled in Glaze config, Sugar’s Vite extension is automatically registered and you can render dev/prod asset tags directly from templates.
Basic usage:
<s-template s:vite="'assets/css/site.css'" />
Multiple entries:
<s-template s:vite="['assets/css/site.css', 'assets/js/site.js']" />
Default entry (from build.vite.defaultEntry or devServer.vite.defaultEntry):
<s-template s:vite="true" />
Behavior by mode:
-
glaze serve --vite(live mode): emits dev server tags (@vite/client+ module entry tags) -
glaze buildwithbuild.vite.enabled: true: resolves and emits production tags from Vite manifest
To scaffold all related files/config quickly:
glaze init my-site --vite
Debug helpers #
Available in all templates:
-
dump(...)– formatted variable dump -
debug(...)– alias fordump -
dd(...)– dump and die
Output is rendered in a styled <pre> block when served via glaze serve.