Developer Guide — Templating, Layouts, and Partials

Complete developer reference for Dax Pro templates: the DAX template syntax, layout inheritance, partial composition, variable scope, conditionals, loops, and the file structure of templates/. Customize layouts, build new partials, extend the theme, and modify the engine-facing behavior.

Who This Chapter Is For

Everything up to this chapter has been for site owners: people who edit content, configure the site, and deploy. This chapter is for developers: people who want to change how the site looks, add new sections, build new layouts, or extend the theme beyond what Dax Pro ships with.

If you have never edited a .dax file before, this chapter is your starting point. If you have edited templates in Jekyll, Hugo, Eleventy, or Liquid, most of this will feel familiar — the syntax is similar, the concepts are the same.

By the end of this chapter, you will:

  • Understand the full template syntax Dax Pro supports
  • Know the difference between layouts and partials
  • Be able to add a new section to the homepage
  • Be able to create a new layout for a new page type
  • Understand variable scope and the {{ }} / {% %} delimiters
  • Know what is possible and what is not (Dax Pro is deliberately simple)

  • The Template System at a Glance

    Dax Pro uses a custom template engine built into the DAX C# engine. It is not Liquid, Handlebars, Mustache, or Blade. It is a small, purpose-built engine that supports exactly the features Dax Pro needs — and no more.

    The engine supports:

  • Include — {% include file.dax %}
  • For — {% for item in list %}...{% endfor %}
  • If — {% if condition %}...{% endif %}
  • Else — {% if condition %}...{% else %}...{% endif %}
  • Variable substitution — {{ variable }}
  • Nested variable access — {{ obj.key.subkey }}
  • Fallback — {{ a or b }}
  • The engine does not support:

  • {% elif %} — use nested {% if %} inside {% else %} instead
  • {% set %} or variable assignment
  • Filters or pipes ({{ name | uppercase }})
  • contains in {% if %} conditions
  • {% for %}...{% else %} (empty loops have no else branch)
  • Arithmetic in templates
  • String concatenation
  • If you need something the engine does not support, the correct fix is to prepare the data in the content layer (frontmatter or _data/), not to add engine features. The engine is intentionally minimal because minimal engines are easier to debug.


    The Template Directory Structure

    All templates live under templates/:

    templates/
    

    ├── layouts/

    │ ├── base.dax

    │ ├── home.dax

    │ ├── post.dax

    │ ├── posts-list.dax

    │ ├── page.dax

    │ ├── contact.dax

    │ ├── service.dax

    │ ├── services-list.dax

    │ ├── tag.dax

    │ └── tags.dax

    └── partials/

    ├── shared/

    │ ├── nav.dax

    │ ├── mobile-drawer.dax

    │ ├── footer.dax

    │ └── seo.dax

    ├── home/

    │ ├── hero.dax

    │ ├── trust.dax

    │ ├── solutions.dax

    │ ├── performance.dax

    │ ├── showcase.dax

    │ ├── pricing.dax

    │ ├── insights.dax

    │ └── contact.dax

    ├── blog/

    │ ├── header.dax

    │ ├── card.dax

    │ └── pagination.dax

    ├── post/

    │ ├── header.dax

    │ ├── cover.dax

    │ ├── tags.dax

    │ └── nav.dax

    ├── page/

    │ ├── header.dax

    │ └── cta-box.dax

    ├── contact/

    │ ├── header.dax

    │ └── box.dax

    ├── service/

    │ ├── header.dax

    │ ├── cta.dax

    │ └── nav.dax

    ├── services/

    │ ├── header.dax

    │ └── pagination.dax

    ├── tag/

    │ └── header.dax

    └── tags/

    └── header.dax

    Two folders, two purposes:

  • layouts/ — one file per page type. A layout defines the outer shell of a page. base.dax is the root layout (the document). Every other layout includes base.dax through its own frontmatter.
  • partials/ — reusable snippets. Partials are included by layouts or by other partials. They are grouped by context: shared/ for things used everywhere, home/ for homepage sections, blog/ for blog-related snippets, and so on.
  • The naming convention:

  • base.dax — the HTML skeleton
  • {type}.dax — a layout for one page type (home.dax, post.dax, page.dax)
  • {type}-list.dax — a layout for a listing (posts-list.dax, services-list.dax)
  • {context}/{name}.dax — a partial specific to a context (home/hero.dax, blog/card.dax)

  • Layout Inheritance

    Dax Pro uses layout chaining. A page's frontmatter declares its layout. That layout declares its own parent layout. The chain continues until base.dax, which has no parent.

    Example — a blog post:

    1. content/posts/my-post.md has layout: "post.dax"

    2. post.dax has frontmatter layout: base.dax

    3. base.dax has no layout

    The engine renders in reverse order:

    1. Start with the Markdown body of my-post.md, converted to HTML

    2. Wrap that HTML in post.dax, replacing {{ content }} with the rendered post

    3. Wrap the result in base.dax, replacing {{ content }} with the rendered post

    The final output is a complete HTML document.

    Example — a service page:

    1. content/services/my-service.md has layout: "service.dax"

    2. service.dax has frontmatter layout: base.dax

    3. base.dax has no layout

    Same pattern. Two levels.

    Example — the homepage:

    1. content/index.md has layout: "home.dax"

    2. home.dax has frontmatter layout: base.dax

    3. base.dax has no layout

    Same pattern.

    The pattern is consistent: every layout except base.dax has exactly one parent. Layout chains never exceed three levels. The engine enforces a maximum of five levels, but Dax Pro uses two.

    To add a new layout — for example, a "thanks" page after contact form submission:

    1. Create templates/layouts/thanks.dax with layout: base.dax in its frontmatter

    2. Create content/thanks.md with layout: "thanks.dax" in its frontmatter

    That is the entire setup. No registration, no configuration.

    To remove a layout chain level — flatten post.dax into base.dax — you would merge the content of both layouts and update all post frontmatter from post.dax to base.dax. Rarely necessary; the current structure is clean.


    The base.dax Layout

    base.dax is the root of every page. It renders the element, the with SEO tags, the navigation bar, the mobile drawer, the main content area, and the footer.

    A minimal base.dax looks like this:

    <!DOCTYPE html>
    

    <html lang="en">

    <head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    {% include shared/seo.dax %}

    <link rel="stylesheet" href="/css/style.css">

    <link rel="alternate" type="application/rss+xml" title="{{ metadata.title }}" href="/rss.xml">

    </head>

    <body>

    {% include shared/nav.dax %}

    {% include shared/mobile-drawer.dax %}

    {{ content }}

    {% include shared/footer.dax %}

    <script src="/js/main.js" defer></script>

    </body>

    </html>

    Key concepts:

  • {% include shared/seo.dax %} — inserts the SEO partial (title, description, OpenGraph, JSON-LD)
  • {% include shared/nav.dax %} — inserts the navigation bar
  • {% include shared/mobile-drawer.dax %} — inserts the mobile menu drawer
  • {{ content }} — the placeholder where the child layout's content is inserted
  • {% include shared/footer.dax %} — inserts the footer