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 looks for a Latte template matching the page's view property (defaults to default), preferring site/ over phlat/:

site/views/{view}.latte     ← checked first
phlat/views/{view}.latte    ← fallback

If no matching template is found, a NotFoundException is thrown.

Template variables

Every view receives $page and $app automatically, plus anything returned by controllers. The framework app controller also provides $styles, $scripts, and $logo.

{$page->title}
{$page->url()}
{$app->config->get('site_name')}

Layout inheritance

Use Latte's {layout} and {block} tags to share a common outer shell:

{* site/views/post.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/root.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. For plain fields it HTML-escapes. For typed fields (markdown, code, image) it renders through the field's view template:

{$page->title}          {* escaped string output *}
{$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/theme-picker.latte', options: ['default', 'dark', 'dim']}

Variables after the comma are passed to the partial as locals.

<?php
// site/views/article.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->url()}" alt="">
//     {/if}
//
//     {$page->body|render}
// </article>
// {/block}