Overview
Chirp templates use Kida's block system:{% extends %}, {% block %},
{% include %}, and {% call %}. This guide covers patterns for using each
construct and composing pages inside a shell.
Pick a shell first. Your templates
{% extends %}one of three root layouts (boost.html,shell.html, or chirp-ui'sapp_shell_layout.html). See Shells for the decision table and thehx-selectdistinction. This guide assumes a shell is already chosen.
For fixed column ratios on page content (grid() + block(span=…)),
see the Chirp UI package docs/LAYOUT-OVERFLOW.md (section
Fixed columns: grid presets).
Extending a Shell
Whatever shell you picked, the extension shape is the same:
{% extends "chirp/layouts/boost.html" %} {# or shell.html, or chirpui/app_shell_layout.html #}
{% block title %}My App{% end %}
{% block content %}
<p>Page content goes here.</p>
{% end %}
{% block body_after %}
<script>/* app-specific JS */</script>
{% end %}
The core shells expose the same block names — title, head, body_before,
content, sse_scope, body_after— so migrating between them is a
one-line change. chirp-ui'sapp_shell_layout.htmladds shell-specific
blocks (brand, sidebar, page_root, page_content); see
App Shells.
Important: Putsse_scope outside #main. If it's inside content,
it gets replaced on navigation and live updates stop.
Outer vs Inner Content
For SSE swap targets and fragment structure:
- Outer element — The
sse-swaptarget. Holds padding, border, layout. Stays in the DOM; its innerHTML is replaced. - Inner element — The fragment block content. No duplicate padding or border.
<!-- Outer: swap target, has padding/border; hx-target="this" when sse-connect has hx-disinherit -->
<div class="answer" sse-swap="answer" hx-target="this">
<!-- Inner: fragment renders this; no extra padding -->
<div class="answer-body" data-copy-text="...">
<div class="answer-content prose">...</div>
<button class="copy-btn">Copy</button>
</div>
</div>
Avoid nesting two elements with the same padding/border — it causes double spacing. Keep .copy-btn in normal flow (no position: absolute) so it stays with its answer.
When to Use Each Construct
| Construct | Use for |
|---|---|
{% extends %} |
Base layout (boost, custom shell). One per template. |
{% block %} |
Overridable sections. Child templates fill or extend. |
{% include %} |
Reusable partials (headers, footers, cards). No block params. |
{% call %} |
Macros with parameters. Use with{% def %}. |
Blocks define slots; includes pull in full partials; call/def are parameterized components.
Block Inheritance
Child templates override blocks by redefining them:
{% extends "base.html" %}
{% block content %}
{{ super() }}
<p>Additional content after parent block.</p>
{% end %}
{{ super() }}renders the parent block's content. Omit it to replace entirely.
Next Steps
- Shells — Pick the right root layout (boost, shell, app-shell) and the
hx-selectdistinction - Fragments — Block-level rendering for htmx
- App Shells — Persistent sidebar layout with SPA navigation
- RAG Demo — Full layout example with SSE and
shell.html