Themes — A Deep Dive
Layouts, partials, blocks — and the gotchas.
A Squilla theme is a self-contained directory under themes/<slug>/. There is no separate “theme installer” — dropping the directory on disk and activating the theme is the whole flow. This post walks through what's inside one and which gotchas trip people up first.
The directory
themes/<slug>/
theme.json # manifest
layouts/*.html # page-level templates
partials/*.html # reusable fragments
blocks/<slug>/ # content blocks (block.json + view.html)
assets/ # CSS, JS, images
scripts/theme.tengo # seed entry point
templates/*.json # page templates for editors
forms/*.html # themed form layoutsThree template types, three contexts
This is the #1 thing to internalise before you start writing markup.
- Layouts are page-level. They see
.node,.app,.userat the top level. - Partials are reusable fragments included via
{{renderLayoutBlock "slug"}}. Same context as the layout, plus.partial. - Block views are atomic content units. They see only the block's own field values at root.
.appis not in scope. Want a site setting? Pass it down via the seed.
Activation runs theme.tengo
Activating a theme runs scripts/theme.tengo in a sandboxed Tengo VM. The script registers node types, taxonomies, settings, and seeds pages — all idempotently. There is no server restart. The kernel atomically swaps the layout tree.
The pattern that scales is to keep theme.tengo as a small dispatcher and split the actual seeding work into modular sub-scripts:
nodetypes := import("./setup/nodetypes")
pages := import("./seeds/pages")
posts := import("./seeds/posts")
nodetypes.run()
pages.run()
posts.run()The classic gotchas
- Settings keys are dotted strings. Templates can't dot-traverse them — use
{{ index $settings "squilla.brand.name" }}, not{{$settings.squilla.brand.name}}. - Menu items render via
.title, not.label. The Tengo input islabel:, but storage and render usetitle. - block.json schema uses
"key"; Tengo nodetypes.register uses"name". Don't mix them. - Don't write fallback values in templates.
{{or .heading "default"}}hides data bugs. Pass real defaults viafields_datain the seed.
Read themes/squilla/scripts/theme.tengo in the repo for a working reference — it's the theme rendering this site, and it exercises every part of the API.