Examples

Data Model

Hwaro's template system centers on three core types: Site, Section, and Page. This page is a template-side reference — all properties and variables you can use when building templates. For how to write content and set front matter fields, see Writing.

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>

For custom markup, iterate section.pages directly:

<ul>
{% for p in section.pages %}
  <li>
    <a href="{{ p.url }}">{{ p.title }}</a>
    {% if p.date %}<time>{{ p.date }}</time>{% endif %}
  </li>
{% endfor %}
</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 reading order
page.higher Page? Next page in reading order
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

Navigation follows the flat reading order across the entire site, similar to mdBook or Docusaurus. Pages are ordered depth-first through the section tree: section index → section pages → subsections (recursive). Within each section, pages are sorted by the section's sort_by setting (weight, date, or title).

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

Pre-rendered HTML (backward compatible):

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)
pagination_seo_links <link rel="prev/next"> tags
<head>
  {{ og_all_tags | safe }}
  {{ canonical_tag | safe }}
  {{ hreflang_tags | safe }}
  {{ pagination_seo_links | safe }}
</head>

Structured data for custom meta tag markup:

Property Type Description
seo.canonical_url String Full canonical URL (base_url + page URL)
seo.og_type String OpenGraph type (default: "article")
seo.og_image String Resolved absolute image URL
seo.twitter_card String Twitter card type (default: "summary_large_image")
seo.twitter_site String Twitter site handle
seo.twitter_creator String Twitter creator handle
seo.fb_app_id String Facebook App ID
seo.hreflang Array Same as page.translations

Page title, description, URL, and image are available as page.title, page.description, page.url, page.image. The seo object provides computed values specific to SEO (resolved URLs, config values).

<head>
  <link rel="canonical" href="{{ seo.canonical_url }}">
  <meta property="og:title" content="{{ page.title }}">
  <meta property="og:type" content="{{ seo.og_type }}">
  <meta property="og:url" content="{{ seo.canonical_url }}">
  {% if page.description %}
  <meta property="og:description" content="{{ page.description }}">
  {% endif %}
  {% if seo.og_image %}
  <meta property="og:image" content="{{ seo.og_image }}">
  {% endif %}
  <meta name="twitter:card" content="{{ seo.twitter_card }}">
  {% if seo.twitter_site %}
  <meta name="twitter:site" content="{{ seo.twitter_site }}">
  {% endif %}
</head>

Asset Variables

Pre-rendered <link> and <script> tags for convenience. These are generated from your config.toml settings.

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

Table of Contents

Only available when toc = true in front matter.

Pre-rendered HTML (backward compatible):

Variable Type Description
toc String Generated TOC HTML
toc_obj.html String Same TOC HTML in object form
{% if page.toc %}
<aside class="toc">
  {{ toc | safe }}
</aside>
{% endif %}

Structured data for custom TOC markup:

Property Type Description
toc_obj.headers Array Structured TOC header objects
toc_obj.headers[].level Int Heading level (2-6)
toc_obj.headers[].id String Anchor ID
toc_obj.headers[].title String Heading text
toc_obj.headers[].permalink String Full anchor permalink
toc_obj.headers[].children Array Nested child headers (same structure)
{% if page.toc %}
<nav class="toc">
  <ul>
  {% for h in toc_obj.headers %}
    <li>
      <a href="{{ h.permalink }}">{{ h.title }}</a>
      {% if h.children %}
      <ul>
        {% for child in h.children %}
        <li><a href="{{ child.permalink }}">{{ child.title }}</a></li>
        {% endfor %}
      </ul>
      {% endif %}
    </li>
  {% endfor %}
  </ul>
</nav>
{% 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) }}

See Also