Views
Views are Latte templates. A page's view property determines which template renders it and which controller, router, and field definitions apply.
View selection
PhlatPage resolves a page's Latte template from its view property (defaults to default). It checks site/ before phlat/, and within each, the folder form before the flat file:
site/views/{view}/index.latte ← checked first
site/views/{view}.latte ← site flat-file fallback
phlat/views/{view}/index.latte ← framework fallback
phlat/views/{view}.latte ← framework flat-file fallback
A page can also override its view's template outright with its own index.latte, placed directly in the page's own folder. If nothing resolves, a NotFoundException is thrown.
Template variables
Every view receives $page, plus anything returned by the global controller (views/controller.php, the app view) and the page's own view controller, merged together. $app is not implicitly available in templates, a controller has to pass it explicitly if a template needs it.
{$page->title}
{$page->url()}
{$styles} {* only available if a controller returned it *}
Layout inheritance
Use Latte's {layout} and {block} tags to share a common outer shell:
{* site/views/post/index.latte *}
{layout 'site/root.latte'}
{block body}
<article>
<h1>{$page->title}</h1>
<div class="prose">{$page->body|render}</div>
</article>
{/block}
The layout file defines the outer HTML and declares named blocks:
{* site/views/site/index.latte *}
<!DOCTYPE html>
<html>
<head>
<title>{$page->title}</title>
<link rel="stylesheet" href="{$styles}">
</head>
<body>
{block body}{/block}
</body>
</html>
Latte syntax essentials
Assign without output using {var}. {$x = ...} assigns and outputs.
{var $children = $page->children()}
{foreach $children as $child}
<a href="{$child->url()}">{$child->title}</a>
{/foreach}
{if !$page->summary->empty()}
<p>{$page->summary}</p>
{/if}
The |render filter
|render passes a field through its type's render template, checked in this order: site/views/fields/{view}/{name}.latte, site/views/fields/{name}.latte, site/views/fields/{type}.latte. If none exist, it falls back to the plain decoded value:
{$page->title} {* plain string output, escaped by Latte *}
{$page->body|render} {* markdown → HTML, code → highlighted block *}
Always check before rendering optional fields:
{if !$page->body->empty()}
<div class="prose">{$page->body|render}</div>
{/if}
Icons
{icon()} renders an inline SVG from phlat/icons/ by name:
{icon('check')}
{icon('arrow-right', 'w-4 h-4 text-primary')}
The second argument sets the class attribute on the <svg> element.
Partials
{include} pulls in a partial. Paths resolve from the views root:
{include 'site/logo.latte'}
{include 'ui/badge.latte', label: 'New'}
Variables after the comma are passed to the partial as locals.
<?php
// site/views/article/index.latte, shown as PHP for code highlighting.
// Actual file is Latte syntax:
//
// {layout 'site/root.latte'}
//
// {block body}
// <article class="prose">
// {include 'site/title.latte'}
//
// {if !$page->cover->empty()}
// <img src="{$page->cover}" alt="">
// {/if}
//
// {$page->body|render}
// </article>
// {/block}