Layout

A layout defines the base HTML structure of your application. It acts as the root container where all slots, includes, and bindings are applied. The layout is bound once and everything else flows from it.

---

1. Basic Usage

Layouts are loaded through CandidTemplateAdaptor using bind(). The adaptor resolves the file path and loads the HTML into the root CandidTemplate instance.


// Via adaptor singleton — recommended for web applications
$view = CandidTemplateAdaptor::getInstance();
$view->bind('/path/to/themes/default/index.html');

// Access root template for direct bindings
$tpl = $view->getTpl();
---

2. Example Layout File

themes/default/index.html


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>My Application</title>
    <link href="/css/style.css" rel="stylesheet">
</head>
<body>

    <div data-slot="topbar"></div>
    <div data-slot="navbar"></div>
    <div data-slot="content"></div>
    <div data-slot="footer"></div>

    <script src="/js/main.js"></script>
</body>
</html>
The data-slot wrapper elements are completely removed after rendering — only the slot template content remains in the final output. No empty wrapper divs are left behind.
---

3. How Layout Works

bind('index.html')          — load layout into root template
    ↓
slot() / slotIf()           — register slot templates
    ↓
include()                   — register includes
    ↓
pick()                      — queue data bindings
    ↓
build()                     — retrieve root template
    ↓
render()                    — apply all bindings, render final HTML
---

4. Route-Based Layout Binding

Different layouts can be bound based on the current route. The singleton pattern ensures the same instance is used across include.php and page controllers.


// include.php — bind layout based on route
$view = CandidTemplateAdaptor::getInstance();

if (str_contains(getCurrentURL(), '/news/')) {
    $view->bind('/themes/default/index.html');
} else {
    $view->bind('/themes/inapp/index.html');
}

$tpl = $view->getTpl();
updateLink($tpl);
updateScriptSrc($tpl);
---

5. Using Layout with Slots

Register slot templates against data-slot placeholders in the layout. Sub-slots must always be registered on their parent slot — not on the root.


// Top-level slots registered on root via adaptor
$view->slot('topbar', 'topbar');

$navbar = $view->slot('navbar', 'navbar');
$navbar->pick('nav_home')->activeIf($page === 'home');
$navbar->pick('nav_category')->activeIf($page === 'category');

// Sub-slots registered on parent — not on $view
$home = $view->slot('content', 'home');
$home->slot('slider',   'slider');
$home->slot('breaking', 'breaking');
$home->slot('featured', 'featured');
$home->slot('sidebar',  'sidebar');

$view->slot('footer', 'footer');

$tpl = $view->build();
echo $tpl->render();
---

6. Conditional Slots — slotIf()

Skip loading a slot entirely when not needed. The placeholder is removed from the DOM — no file I/O occurs.


// Admin panel only loads if user is admin
$admin = $view->slotIf($user->isAdmin(), 'admin', 'admin-panel');
$admin?->pick('username')->content($user->name);

// Breaking news only shows when available
$home->slotIf($news->hasBreaking(), 'breaking', 'breaking');

// Sidebar conditional on feature flag
$home->slotIf($features->sidebarEnabled(), 'sidebar', 'sidebar');
---

7. Using Layout with Includes

Includes inject a template into an existing element — the element itself is replaced by the include content.


// Layout HTML
<div id="breadcrumb"></div>

// PHP
$breadcrumb = $view->include('#breadcrumb', 'breadcrumb');
$breadcrumb->pick('current')->content('Home');
---

8. Multiple Layouts — Different Themes


// Frontend theme
if (isFrontend()) {
    $view->bind('/themes/default/index.html');
    $tpl = $view->getTpl();
    updateLink($tpl);
    updateScriptSrc($tpl);
}

// Admin theme
if (isAdmin()) {
    $view->bind('/themes/inapp/index.html');
    $tpl = $view->getTpl();
    updateLink($tpl);
    updateScriptSrc($tpl);
}
---

9. Layout Render Flow

root template (index.html)
    ├── slot: topbar     → topbar.html
    ├── slot: navbar     → navbar.html
    ├── slot: content    → home.html
    │       ├── slot: slider   → slider.html
    │       ├── slot: breaking → breaking.html  (slotIf)
    │       ├── slot: featured → featured.html
    │       └── slot: sidebar  → sidebar.html   (slotIf)
    └── slot: footer     → footer.html

Render order:

  1. Root layout HTML is loaded
  2. Includes are resolved and injected
  3. Slots are resolved depth-first and injected
  4. Includes introduced by slots are resolved
  5. Loops are applied
  6. All pick() assignments are applied
  7. Final HTML string is returned
---

10. Rules

---

11. Common Mistakes

❌ Registering sub-slots on root instead of parent template

❌ Missing data-slot in layout — slot silently skipped

❌ Wrong slot name — does not match data-slot value

❌ Duplicate slot names in same layout

❌ Using showIf() for large sections — use slotIf() instead

❌ Calling render() before build()

❌ Forgetting null-safe operator on slotIf() return value
---

12. Best Practices

---

13. Performance Notes

---
Layout is the root of your UI — bind it once in include.php, register slots on their correct parent, and let render() assemble the final HTML cleanly with no leftover placeholder elements.