Data Model

Hwaro's template system centers on three core types: Site, Section, and Page. Understanding their hierarchy is essential for building templates.

Hierarchy

Site
├── Config (title, base_url, ...)
├── Pages[] (standalone pages)
├── Sections[]
│   ├── Pages[] (pages in section)
│   └── Subsections[]
│       ├── Pages[]
│       └── Subsections[] (recursive)
└── Taxonomies{}
    └── Terms{}
        └── Pages[]

Relationships

Site

The root container. Configured in config.toml.

Properties

Property Type Description
site.title String Site title
site.description String Site description
site.base_url String Base URL (no trailing slash)
site.pages Array<Page> All non-section pages
site.sections Array<Section> All section index pages
site.taxonomies Object All taxonomy groups and terms
site.data Object Data loaded from `data/` directory
site.authors Object Aggregated author data

Flat Aliases

Variable Equivalent
site_title site.title
site_description site.description
base_url site.base_url

Data Directory

Hwaro allows you to store auxiliary data in the data/ directory. Files ending in .yml, .yaml, .json, or .toml are automatically loaded and exposed via site.data.

File Structure

data/
├── authors.yml
├── products.json
└── config.toml

Accessing Data

Data is accessed by the filename (without extension).

For example, data/products.json:

[
  {"name": "Widget", "price": 10},
  {"name": "Gadget", "price": 20}
]

Can be accessed in templates:

{% for product in site.data.products %}
  <h2>{{ product.name }}</h2>
  <p>{{ product.price }}</p>
{% endfor %}

Site Authors

Hwaro automatically aggregates all authors defined in the authors front matter field (authors = ["id"]) into site.authors.

Defining Authors

You can enrich author data by creating a data/authors.yml (or .json, .toml) file. Keys must match the author IDs used in the page front matter.

content/my-post.md

---
title: "My Post"
authors: ["john-doe"]
---

data/authors.yml

john-doe:
  name: "John Doe"
  bio: "Creator of things."
  avatar: "/images/john.jpg"

Usage in Templates

The site.authors object contains all authors found on the site. Each author object has:

{% for id, author in site.authors %}
  <div class="author">
    <img src="{{ author.avatar }}" alt="{{ author.name }}">
    <h3>{{ author.name }}</h3>
    <p>{{ author.bio }}</p>

    <h4>Recent Posts</h4>
    <ul>
    {% for p in author.pages %}
      <li><a href="{{ p.url }}">{{ p.title }}</a></li>
    {% endfor %}
    </ul>
  </div>
{% endfor %}

Example

<title>{{ site.title }}</title>
<link rel="canonical" href="{{ site.base_url }}{{ page.url }}">

Section

A directory with _index.md that groups related content.

Properties

Property Type Description
section.title String Section title
section.description String? Section description
section.pages Array<Page> Pages in this section
section.pages_count Int Number of pages
section.list String Pre-rendered HTML list (`section_list`)
section.subsections Array<Section> Child sections
section.assets Array<String> Static files in section
section.page_template String? Default template for pages
section.paginate_path String Pagination URL pattern
section.redirect_to String? Redirect URL

For the current section URL in section.html, use page.url.

Flat Aliases

Variable Equivalent
section_title section.title
section_description section.description
section_list Pre-rendered HTML list of pages

From Front Matter

Property Type Default Description
sort_by String? "date" Sort by: date, weight, title
reverse Bool? false Reverse sort order
paginate Int? Pages per page
transparent Bool false Pass pages to parent
generate_feeds Bool false Generate RSS feed

Iterating Pages

{% for p in section.pages %}
<article>
  <h2><a href="{{ p.url }}">{{ p.title }}</a></h2>
  <time>{{ p.date }}</time>
  {% if p.description %}
  <p>{{ p.description }}</p>
  {% endif %}
</article>
{% endfor %}

Iterating Subsections

{% for sub in section.subsections %}
<div class="category">
  <a href="{{ sub.url }}">{{ sub.title }}</a>
  <span>({{ sub.pages_count }} articles)</span>
</div>
{% endfor %}

Using section_list

For simple listings, use the pre-rendered HTML:

<ul>{{ section_list | safe }}</ul>

Page

An individual content file (.md).

Core Properties

Property Type Description
page.title String Page title
page.description String? Page description
page.url String Relative URL path
page.permalink String? Absolute URL with base_url
page.section String Parent section name
page.date String? Publication date (YYYY-MM-DD)
page.updated String? Last updated date
page.language String Effective language code
page.translations Array<TranslationLink> Language variants

Rendered HTML content is available as the top-level content variable.

Metadata Properties

Property Type Description
page.draft Bool Is draft
page.weight Int Sort weight
page.image String? Featured image path
page.authors Array<String> Author names
page.extra Object Custom front matter fields

Computed Properties

Property Type Description
page.word_count Int Word count
page.reading_time Int Reading time (minutes)
page.summary String? Content before <!-- more -->
page.assets Array<String> Static files in page bundle

Boolean Flags

Property Type Default Description
page.toc Bool false Show table of contents
page.render Bool true Should render
page.is_index Bool Is index file
page.generated Bool false Auto-generated page
page.in_sitemap Bool true Include in sitemap
page.in_search_index Bool true Include in search
Property Type Description
page.lower Page? Previous page in section
page.higher Page? Next page in section
page.ancestors Array<Page> Parent section chain
page.translations Array<TranslationLink> Language variants

Custom Metadata

Property Type Description
page.extra Object Custom front matter fields

Flat Aliases

Variable Equivalent
page_title page.title
page_description page.description
page_url page.url
page_section page.section
page_date page.date
page_image page.image
page_summary page.summary
page_word_count page.word_count
page_reading_time page.reading_time
page_permalink page.permalink
page_authors page.authors
page_weight page.weight
page_language page.language
page_translations page.translations
taxonomy_name Current taxonomy name (taxonomy pages)
taxonomy_term Current taxonomy term (taxonomy term pages)
content Rendered HTML content

page.lower / page.higher

Property Type Description
.title String Page title
.url String Page URL
.description String? Page description
.date String? Page date
<nav class="post-nav">
  {% if page.lower %}
  <a href="{{ page.lower.url }}">← {{ page.lower.title }}</a>
  {% endif %}
  
  {% if page.higher %}
  <a href="{{ page.higher.url }}">{{ page.higher.title }} →</a>
  {% endif %}
</nav>

page.ancestors

Parent sections for breadcrumbs:

<nav class="breadcrumbs">
  <a href="/">Home</a>
  {% for ancestor in page.ancestors %}
  / <a href="{{ ancestor.url }}">{{ ancestor.title }}</a>
  {% endfor %}
  / <span>{{ page.title }}</span>
</nav>

page.translations

Property Type Description
.code String Language code (e.g., "en")
.url String Translated page URL
.title String Title in that language
.is_current Bool Current page's language
.is_default Bool Default language
{% if page.translations %}
<nav class="lang-switcher">
{% for t in page.translations %}
  {% if t.is_current %}
  <span>{{ t.code | upper }}</span>
  {% else %}
  <a href="{{ t.url }}">{{ t.code | upper }}</a>
  {% endif %}
{% endfor %}
</nav>
{% endif %}

Accessing page.extra

Custom metadata from front matter:

+++
title = "Review"

[extra]
rating = 4.5
featured = true
pros = ["Fast", "Reliable"]
+++
{% if page.extra.featured %}
<span class="badge">Featured</span>
{% endif %}

<div class="rating">{{ page.extra.rating }} / 5</div>

<ul>
{% for pro in page.extra.pros %}
  <li>{{ pro }}</li>
{% endfor %}
</ul>

Time Variables

Variable Type Description
current_year Int Current year (e.g., 2025)
current_date String Current date (YYYY-MM-DD)
current_datetime String Current datetime
<footer>&copy; {{ current_year }} {{ site.title }}</footer>

SEO Variables

Variable Description
og_tags OpenGraph meta tags
twitter_tags Twitter Card meta tags
og_all_tags Both OG and Twitter tags
canonical_tag Canonical link tag
hreflang_tags Hreflang alternate link tags (multilingual)
<head>
  {{ og_all_tags | safe }}
  {{ canonical_tag | safe }}
  {{ hreflang_tags | safe }}
</head>

Asset Variables

Variable Description
highlight_css Syntax highlighting CSS
highlight_js Syntax highlighting JS
highlight_tags Both CSS and JS
auto_includes_css Auto-included CSS
auto_includes_js Auto-included JS
auto_includes All auto-includes
<head>
  {{ highlight_css | safe }}
  {{ auto_includes_css | safe }}
</head>
<body>
  ...
  {{ highlight_js | safe }}
  {{ auto_includes_js | safe }}
</body>

Table of Contents

Variable Type Description
toc String Generated TOC HTML
toc_obj.html String Same TOC HTML in object form

Only available when toc = true in front matter:

{% if page.toc %}
<aside class="toc">
  {{ toc | safe }}
</aside>
{% endif %}

Taxonomy Variables

Available in taxonomy templates:

Variable Type Description
taxonomy_name String Taxonomy name (e.g., "tags")
taxonomy_term String Current term name
taxonomy_terms Array All terms (in taxonomy.html)
taxonomy_pages Array<Page> Pages for term

Type Reference

Quick Reference

Type Description
String Text value
String? Optional text (may be nil)
Int Integer number
Bool true/false
Array<T> List of type T
Object Key-value map

Template Checking

{# Check for nil #}
{% if page.description %}...{% endif %}

{# Check for empty array #}
{% if page.authors %}...{% endif %}

{# Check for empty string #}
{% if page.description is present %}...{% endif %}

{# Default value #}
{{ page.description | default(value=site.description) }}

Example Templates

page.html

{% extends "base.html" %}

{% block content %}
<article>
  <h1>{{ page.title }}</h1>
  
  <div class="meta">
    <time>{{ page.date }}</time>
    {% if page.authors %}
    <span>by {{ page.authors | join(", ") }}</span>
    {% endif %}
    <span>{{ page.reading_time }} min read</span>
  </div>
  
  {% if page.toc %}
  <nav class="toc">{{ toc | safe }}</nav>
  {% endif %}
  
  <div class="content">
    {{ content | safe }}
  </div>
  
  {% if page.lower or page.higher %}
  <nav class="post-nav">
    {% if page.lower %}
    <a href="{{ page.lower.url }}">← {{ page.lower.title }}</a>
    {% endif %}
    {% if page.higher %}
    <a href="{{ page.higher.url }}">{{ page.higher.title }} →</a>
    {% endif %}
  </nav>
  {% endif %}
</article>
{% endblock %}

section.html

{% extends "base.html" %}

{% block content %}
<section>
  <h1>{{ section.title }}</h1>
  {% if section.description %}
  <p class="lead">{{ section.description }}</p>
  {% endif %}
  
  {{ content | safe }}
  
  <h2>Articles ({{ section.pages_count }})</h2>
  <ul class="article-list">
  {% for p in section.pages %}
    <li>
      <a href="{{ p.url }}">{{ p.title }}</a>
      {% if p.date %}<time>{{ p.date }}</time>{% endif %}
    </li>
  {% endfor %}
  </ul>
  
  {% if section.subsections %}
  <h2>Categories</h2>
  <ul>
  {% for sub in section.subsections %}
    <li>
      <a href="{{ sub.url }}">{{ sub.title }}</a>
      ({{ sub.pages_count }})
    </li>
  {% endfor %}
  </ul>
  {% endif %}
  
  {{ pagination | safe }}
</section>
{% endblock %}

See Also