Mandatory files — the renderer/builder assume these exist:
layouts/main.fluid— Why: Renderer throws"Layout not found"without it.templates/home.fluid— Why: The/route.templates/404.fluid— Why: The fallback for unknown routes and missing templates — without it, unknown paths throw instead of rendering a 404.templates/page.fluid— Why: CMS pages render through it.templates/spaces.fluid,templates/space-detail.fluid,templates/locations.fluid,templates/location.fluid,templates/contact.fluid,templates/attributes.fluid— Why: One per route in §3 — a missing one means those URLs render the 404 template.templates/embed-spaces.fluid— Why:/embed/spaces(and legacy/embed/units) swap to it.assets/theme.css,assets/theme.js— Why: Inlined by the layout; missing files inline as empty strings (site renders unstyled).schema/settings.json— Why: Without it the builder's design panel is empty.schema/theme.json— Why: Manifest — name/version sync and the update-available signal.schema/attribute-icons.json— Why:{ "icons": { key: "<svg .../>" }, "keywords": { "security": "lock", ... }, "default": "check" }— feedsattr_icon/attr_icon_key; without it every attribute renders the built-in check icon.snippets/section-style.fluid— Why: The style-override applier (§5.3).
The layout contract — layouts/main.fluid must:
- Emit
{{ content_for_layout | raw }}inside<main>, and inline the assets:
```liquid
...
{{ content_for_layout | raw }}
...
```
- Translate
Settings.Design.*into CSS variables on:root, with defaults, falling back to the legacy typed settings where they exist. From origin'smain.fluid:
liquid :root { --primary-color: {{ Settings.Design.primaryColor | default: Settings.PrimaryColor | default: "#2563eb" }}; --secondary-color: {{ Settings.Design.secondaryColor | default: Settings.SecondaryColor | default: "#1e40af" }}; --bg-color: {{ Settings.Design.bgColor | default: "#ffffff" }}; --text-color: {{ Settings.Design.textColor | default: "#1a1a1a" }}; --font-family: {{ Settings.Design.bodyFont | default: Settings.FontFamily | default: "system-ui, -apple-system, sans-serif" }}; --heading-scale: {{ Settings.Design.headingScale | default: "1" }}; --button-radius: {{ Settings.Design.buttonRadius | default: "8px" }}; --card-radius: {{ Settings.Design.cornerRadius | default: "12px" }}; --container-width: {{ Settings.Design.containerWidth | default: "1200px" }}; }
theme.css must consume these variables (and the section-override set from §5.3) — hard-coded colours in CSS mean the builder's colour controls silently do nothing. If you support colorMode, handle it the way origin does: data-theme="light|dark" on <html>, plus a prefers-color-scheme media block for auto.
- Handle SEO/meta/analytics from
Settings(title/description fallbacks,AllowIndexing→noindex, canonical, OG/Twitter tags, GTM/GA,CustomCssCode,CustomHeadHtml) — see origin'smain.fluidhead for the reference implementation. The renderer resolves all the per-page overlays before your template runs; you just print the values. - Include the chrome partials (
announcement-bar,top-bar,header,footer,quote-modal) — or your own equivalents.
The section wrapper — every sectioned template repeats the §2.2 loop verbatim(data-alyta-section + data-alyta-type + alyta-hide-mobile + {% include 'section-style' %}).This is the contract the builder preview depends on for click-to-select and highlight.
Links — everything internal is under /t/. Hard-code it (href="/t/spaces") or pipestored URLs through storefront_url.
Section defaults — ThemeV2SectionDefaults (C#) defines the canonical section compositionfor home (hero, locations-strip, category-tabs, space-grid, why-choose-us, testimonials,faq, cta), contact (contact-form, map) and attributes (attributes, cta). These are thefallbacks for pages whose stored section lists are empty, so a new theme must at minimum shipsection files with those names — or change the defaults in C# alongside the theme.
Appendix: quick-start checklist for a new theme
- Copy
wwwroot/themes/origin/and renamename/versioninschema/theme.jsonandschema/settings.json. - Keep the seven-folder structure — the file store rejects anything else.
- Keep
layouts/main.fluid's contract: CSS variable block fromSettings.Design.*,{{ theme_css | raw }}/{{ theme_js | raw }}inlining,{{ content_for_layout | raw }}. - Keep the section wrapper loop (with
data-alyta-sectionand{% include 'section-style' %}) in every sectioned template. - Make
theme.cssconsume the variables — brand, neutrals, typography, radii, and the section-override set (--text-color,--heading-color,--bg-color,--surface-color,--button-bg,--button-hover-bg,--button-text). - Give every section an inline
{% schema %}with sensibledefaults anddefaultBlocks— the hydrator makes them render-safe and the builder shows real example content. - Keep
layouts/embed.fluidwith<base target="_top">and thealyta:embed:resizepublisher. - Test the routes in §3 — including
/t/embed/spaces, a location-scoped listing, a CMS page slug, and a nonsense URL (should hit your404.fluid). - Remember every save in the editor bumps the cache version — if you edit blobs any other way, nothing invalidates and you will stare at stale HTML for up to 15 minutes.