Chirp uses Kida'stemplate_metadata()to introspect templates at build time.
Block names, regions, and dependencies come from the AST — Chirp never hard-codes
which blocks exist. That enables declarative OOB regions, block validation, and
layout contracts without framework-specific configuration.
Overview
- Template source → Kida parses and analyzes the AST
- TemplateMetadata → Blocks, regions,
depends_on,cache_scopeper block - build_layout_contract() → Discovers
*_oobblocks, extracts metadata - LayoutContract → Cached per template; drives OOB rendering on boosted navigation
OOB Discovery
Chirp does not hard-code which blocks to render as OOB. Instead, it uses Kida's
template_metadata():
build_layout_contract()callstemplate_metadata()on the root layout- Blocks named
*_oob(or regions with that suffix) are identified as OOB candidates - Each block's
cache_scopeanddepends_onfromBlockMetadatadetermine rendering behavior:cache_scope: "site"→ skip (static, doesn't change per page)depends_on: {"page_title"}→ skip ifpage_titlenot in context
- The
LayoutContractis cached per template for efficiency
Adding new OOB regions requires only:
- Add a
{% region name_oob(params) %}...{% end %}to the layout - Map the region name to a target DOM ID
ChirpUI pre-mapsbreadcrumbs_oob, sidebar_oob, and title_oobto
chirpui-topbar-breadcrumbs, chirpui-sidebar-nav, and chirpui-document-title.
For custom regions, usecustom_oob → target custom(Chirp derives the target
fromblock_name.removesuffix("_oob")), or add an entry to _OOB_TARGET_MAPin
Chirp when using ChirpUI shell IDs.
Block Validation
Before rendering a fragment, Chirp can validate that the requested block exists:
# When validate_blocks=True (e.g. in development)
meta = adapter.template_metadata(view.template)
if view.block not in meta.blocks:
raise KeyError(f"Block '{view.block}' not found in template '{view.template}'")
This uses meta.blocksfrom the AST — no runtime template loading needed for
validation.
Regions
Kida's{% region %} construct compiles to BOTH a block (for render_block)
AND a callable (for{{ name(args) }}). Use regions instead of separate blocks
and defs:
- One definition serves both app shell slots and OOB
render_block() - Parameters make regions self-contained:
{% region sidebar_oob(current_path="/") %} - Callable:
{{ sidebar_oob(current_path=current_path | default("/")) }}in slots - Renderable:
render_block("sidebar_oob", current_path=...)for OOB updates
Chirp usesmeta.regions()when available to prefer region-typed blocks for OOB
discovery, falling back to the*_oobnaming convention for plain blocks.
Migrating from blocks to regions
If your layout uses{% block name_oob %}...{% end %}with duplicated content in
app shell slots, migrate to regions for a single definition:
Before — block + duplication in slots:
{% block breadcrumbs_oob %}
{{ breadcrumbs(breadcrumb_items) }}
{% end %}
{% call app_shell() %}
{% slot topbar %}
{{ breadcrumbs(breadcrumb_items) }} {# duplicated #}
{% end %}
{% end %}
After — region + call in slots:
{% region breadcrumbs_oob(breadcrumb_items=[{"label":"Home","href":"/"}]) %}
{{ breadcrumbs(breadcrumb_items) }}
{% end %}
{% call app_shell() %}
{% slot topbar %}
{{ breadcrumbs_oob(breadcrumb_items=breadcrumb_items | default([{"label":"Home","href":"/"}])) }}
{% end %}
{% end %}
Checklist:
- Convert each
{% block name_oob %}...{% end %}to{% region name_oob(params) %}...{% end %} - Update app shell slots to call the region:
{{ name_oob(...) }}instead of duplicating content - Remove redundant
_page_layoutempty block overrides — Chirp suppresses OOB on full-page viablock_overrides
Metadata Fields Used
| BlockMetadata field | Chirp use |
|---|---|
blocks |
Block existence, OOB discovery |
regions() |
Prefer region-typed OOB blocks |
depends_on |
Skip OOB when required context missing |
cache_scope |
Future: cache page-level fragments |
is_region |
Filter to region blocks for OOB |
region_params |
(Reserved for call validation) |
Adapter Contract
Chirp'sTemplateAdapter protocol requires template_metadata(template_name)
returning an object with:
blocks:dict[str, BlockMetadata]ordict[str, Any]withcache_scope,depends_onattributesregions(): optional; returnsdict[str, BlockMetadata]of region blocks
The Kida adapter returns fullTemplateMetadata. A Jinja2 adapter would return
None, and Chirp falls back to well-known ChirpUI OOB blocks.
Next Steps
- Fragments — How Chirp finds blocks
- App Shells — OOB regions in practice
- Kida Framework Integration — Chirp as consumer