Developer docs  /  Themes

New theme checklist

Mandatory files — the renderer/builder assume these exist:

  • layouts/main.fluidWhy: Renderer throws "Layout not found" without it.
  • templates/home.fluidWhy: The / route.
  • templates/404.fluidWhy: The fallback for unknown routes and missing templates — without it, unknown paths throw instead of rendering a 404.
  • templates/page.fluidWhy: CMS pages render through it.
  • templates/spaces.fluid, templates/space-detail.fluid, templates/locations.fluid, templates/location.fluid, templates/contact.fluid, templates/attributes.fluidWhy: One per route in §3 — a missing one means those URLs render the 404 template.
  • templates/embed-spaces.fluidWhy: /embed/spaces (and legacy /embed/units) swap to it.
  • assets/theme.css, assets/theme.jsWhy: Inlined by the layout; missing files inline as empty strings (site renders unstyled).
  • schema/settings.jsonWhy: Without it the builder's design panel is empty.
  • schema/theme.jsonWhy: Manifest — name/version sync and the update-available signal.
  • schema/attribute-icons.jsonWhy: { "icons": { key: "<svg .../>" }, "keywords": { "security": "lock", ... }, "default": "check" } — feeds attr_icon/attr_icon_key; without it every attribute renders the built-in check icon.
  • snippets/section-style.fluidWhy: The style-override applier (§5.3).

The layout contractlayouts/main.fluid must:

  1. Emit {{ content_for_layout | raw }} inside <main>, and inline the assets:

```liquid

...

{{ content_for_layout | raw }}

...

```

  1. Translate Settings.Design.* into CSS variables on :root, with defaults, falling back to the legacy typed settings where they exist. From origin's main.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.

  1. Handle SEO/meta/analytics from Settings (title/description fallbacks, AllowIndexingnoindex, canonical, OG/Twitter tags, GTM/GA, CustomCssCode, CustomHeadHtml) — see origin's main.fluid head for the reference implementation. The renderer resolves all the per-page overlays before your template runs; you just print the values.
  2. 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 defaultsThemeV2SectionDefaults (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

  1. Copy wwwroot/themes/origin/ and rename name/version in schema/theme.json and schema/settings.json.
  2. Keep the seven-folder structure — the file store rejects anything else.
  3. Keep layouts/main.fluid's contract: CSS variable block from Settings.Design.*, {{ theme_css | raw }} / {{ theme_js | raw }} inlining, {{ content_for_layout | raw }}.
  4. Keep the section wrapper loop (with data-alyta-section and {% include 'section-style' %}) in every sectioned template.
  5. Make theme.css consume 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).
  6. Give every section an inline {% schema %} with sensible defaults and defaultBlocks — the hydrator makes them render-safe and the builder shows real example content.
  7. Keep layouts/embed.fluid with <base target="_top"> and the alyta:embed:resize publisher.
  8. Test the routes in §3 — including /t/embed/spaces, a location-scoped listing, a CMS page slug, and a nonsense URL (should hit your 404.fluid).
  9. 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.