Templates Guide ¶
markata-go uses pongo2, a Django/Jinja2-like template engine for Go. This guide covers everything you need to know about creating and customizing templates.
Table of Contents ¶ #
- Overview
- Template Location
- Template Syntax
- Available Variables
- Built-in Filters
- Template Inheritance
- Including Partials
- Post vs Feed Templates
- Custom Templates Per Post
- Complete Examples
Overview ¶ #
Templates wrap your rendered Markdown content in HTML layouts. The template system supports:
- Template inheritance - Base templates with extendable blocks
- Includes - Reusable partial templates
- Variables and expressions - Access post, config, and feed data
- Control flow - Conditionals and loops
- Filters - Transform data for display
- Custom templates per post - Override templates in frontmatter
Template Location ¶ #
Templates are loaded from these locations (in order of priority):
- Project templates:
templates/directory in project root - Theme templates:
themes/{theme}/templates/for custom themes - Default theme:
themes/default/templates/as fallback
my-site/
├── templates/ # Project templates (highest priority)
│ ├── base.html
│ ├── post.html
│ ├── feed.html
│ └── partials/
│ ├── header.html
│ ├── footer.html
│ └── card.html
├── themes/
│ └── my-theme/
│ └── templates/ # Theme templates
└── posts/
Configure the templates directory in markata-go.toml:
[markata-go]
templates_dir = "templates"
Template Syntax ¶ #
Variables ¶ #
Access data using double curly braces:
{{ post.Title }}
{{ config.URL }}
{{ post.Date }}
Attribute Access ¶ #
{{ post.Title }} {# Direct attribute access #}
{{ post.Extra.custom_field }} {# Nested attributes #}
Filters ¶ #
Transform values using the pipe (|) operator:
{{ post.Title|upper }}
{{ post.Description|truncate:160 }}
{{ post.Date|date_format:"January 2, 2006" }}
{{ post.Tags|join:", " }}
{{ post.ArticleHTML|safe }}
{{ value|default_if_none:"N/A" }}
Comments ¶ #
{# This is a comment and won't appear in output #}
{#
Multi-line
comment
#}
Control Flow ¶ #
Conditionals ¶ #
{% if post.Published %}
<span class="status">Published</span>
{% elif post.Draft %}
<span class="status">Draft</span>
{% else %}
<span class="status">Private</span>
{% endif %}
Loops ¶ #
{% for tag in post.Tags %}
<a href="/tags/{{ tag|slugify }}/">{{ tag }}</a>
{% if not forloop.Last %}, {% endif %}
{% endfor %}
{% for post in posts %}
<li>{{ post.Title }}</li>
{% empty %}
<li>No posts found</li>
{% endfor %}
Loop Variables ¶ #
Inside {% for %} loops, these variables are available:
| Variable | Description |
|---|---|
forloop.Counter |
Current iteration (1-indexed) |
forloop.Counter0 |
Current iteration (0-indexed) |
forloop.First |
True if first iteration |
forloop.Last |
True if last iteration |
forloop.Revcounter |
Iterations remaining (1-indexed) |
Available Variables ¶ #
Post Context ¶ #
When rendering individual posts, these variables are available:
| Variable | Type | Description |
|---|---|---|
post |
object | The post being rendered |
post.Title |
string | Post title |
post.Slug |
string | URL slug |
post.Href |
string | Relative URL path (e.g., /my-post/) |
post.Date |
time | Publication date |
post.Published |
bool | Whether the post is published |
post.Draft |
bool | Whether the post is a draft |
post.Tags |
[]string | List of tags |
post.Description |
string | Post description |
post.Content |
string | Raw Markdown content |
post.ArticleHTML |
string | Rendered HTML content (use with |safe) |
post.Extra |
map | Additional frontmatter fields |
body |
string | Rendered article HTML (alias for post.ArticleHTML) |
config |
object | Site configuration |
Config Context ¶ #
Site configuration is available via config:
| Variable | Type | Description |
|---|---|---|
config.Title |
string | Site title |
config.Description |
string | Site description |
config.URL |
string | Site base URL |
config.Author |
string | Site author |
Shorthand aliases are also available:
| Alias | Equivalent |
|---|---|
site_title |
config.Title |
site_url |
config.URL |
site_description |
config.Description |
site_author |
config.Author |
Feed Context ¶ #
When rendering feeds/archives, these additional variables are available:
| Variable | Type | Description |
|---|---|---|
feed |
object | Feed configuration |
feed.Title |
string | Feed title |
feed.Description |
string | Feed description |
feed.Slug |
string | Feed slug |
feed.Posts |
[]post | All posts in the feed |
page |
object | Current page info |
page.Number |
int | Current page number |
page.Posts |
[]post | Posts on this page |
page.HasPrev |
bool | Whether there’s a previous page |
page.HasNext |
bool | Whether there’s a next page |
page.PrevURL |
string | URL to previous page |
page.NextURL |
string | URL to next page |
posts |
[]post | Posts on current page (alias for page.Posts) |
Built-in Filters ¶ #
Date Formatting ¶ #
| Filter | Example | Output |
|---|---|---|
date_format |
{{ date|date_format:"2006-01-02" }} |
2024-01-15 |
date_format |
{{ date|date_format:"January 2, 2006" }} |
January 15, 2024 |
rss_date |
{{ date|rss_date }} |
RFC 1123Z format for RSS |
atom_date |
{{ date|atom_date }} |
RFC 3339 format for Atom |
Note: Go uses reference time formatting. Common formats:
| Format | Go Pattern |
|---|---|
| 2024-01-15 | 2006-01-02 |
| January 15, 2024 | January 2, 2006 |
| Jan 15, 2024 | Jan 2, 2006 |
| 15 Jan 2024 | 02 Jan 2006 |
| 2024-01-15T10:30:00Z | 2006-01-02T15:04:05Z07:00 |
String Manipulation ¶ #
| Filter | Example | Description |
|---|---|---|
slugify |
{{ "Hello World"|slugify }} |
Outputs hello-world |
truncate |
{{ text|truncate:100 }} |
Truncate to 100 characters with ellipsis |
truncatewords |
{{ text|truncatewords:20 }} |
Truncate to 20 words |
upper |
{{ text|upper }} |
Convert to uppercase |
lower |
{{ text|lower }} |
Convert to lowercase |
title |
{{ text|title }} |
Title case |
striptags |
{{ html|striptags }} |
Remove HTML tags |
Collections ¶ #
| Filter | Example | Description |
|---|---|---|
length |
{{ list|length }} |
Get length |
first |
{{ list|first }} |
Get first element |
last |
{{ list|last }} |
Get last element |
join |
{{ list|join:", " }} |
Join with separator |
reverse |
{{ list|reverse }} |
Reverse order |
sort |
{{ list|sort }} |
Sort alphabetically |
HTML/Text ¶ #
| Filter | Example | Description |
|---|---|---|
safe |
{{ html|safe }} |
Mark HTML as safe (don’t escape) |
escape |
{{ text|escape }} |
HTML escape (default behavior) |
plaintext |
{{ html|plaintext }} |
Convert HTML to clean plain text (entities decoded, tags stripped, links as footnotes) |
linebreaks |
{{ text|linebreaks }} |
Convert newlines to <p> and <br> |
linebreaksbr |
{{ text|linebreaksbr }} |
Convert newlines to <br> |
URLs ¶ #
| Filter | Example | Description |
|---|---|---|
urlencode |
{{ path|urlencode }} |
URL encode |
absolute_url |
{{ post.Href|absolute_url:config.URL }} |
Convert to absolute URL |
Default Values ¶ #
| Filter | Example | Description |
|---|---|---|
default_if_none |
{{ value|default_if_none:"fallback" }} |
Provide fallback for nil/empty |
default |
{{ value|default:"fallback" }} |
pongo2 built-in default |
Template Inheritance ¶ #
Template inheritance lets you create a base layout that child templates extend.
Base Template ¶ #
{# templates/base.html #}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}{{ config.Title }}{% endblock %}</title>
<meta name="description" content="{% block description %}{{ config.Description }}{% endblock %}">
{% block head %}{% endblock %}
</head>
<body>
{% block header %}
<header>
<nav>
<a href="/">{{ config.Title }}</a>
<a href="/blog/">Blog</a>
<a href="/about/">About</a>
</nav>
</header>
{% endblock %}
<main>
{% block content %}{% endblock %}
</main>
{% block footer %}
<footer>
<p>© {{ config.Author }}</p>
</footer>
{% endblock %}
{% block scripts %}{% endblock %}
</body>
</html>
Extending the Base ¶ #
{# templates/post.html #}
{% extends "base.html" %}
{% block title %}{{ post.Title }} | {{ config.Title }}{% endblock %}
{% block description %}{{ post.Description|default_if_none:config.Description }}{% endblock %}
{% block head %}
<meta property="og:title" content="{{ post.Title }}">
<meta property="og:type" content="article">
<meta property="og:url" content="{{ config.URL }}{{ post.Href }}">
{% if post.Date %}
<meta property="article:published_time" content="{{ post.Date|atom_date }}">
{% endif %}
{% endblock %}
{% block content %}
<article>
{{ body|safe }}
</article>
{% endblock %}
Using {{ block.super }} ¶ #
Include parent block content with {{ block.super }}:
{% block scripts %}
{{ block.super }}
<script src="/js/post.js"></script>
{% endblock %}
Including Partials ¶ #
Partials are reusable template fragments. Use {% include %} to embed them:
{% include "partials/header.html" %}
{% include "partials/card.html" %}
{% include "partials/footer.html" %}
Example Partial: Card ¶ #
{# templates/partials/card.html #}
<article class="card">
<a href="{{ post.Href }}">
{% if post.Extra.cover_image %}
<img src="{{ post.Extra.cover_image }}" alt="{{ post.Title }}">
{% endif %}
<h2>{{ post.Title }}</h2>
</a>
{% if post.Description %}
<p>{{ post.Description }}</p>
{% endif %}
<footer>
{% if post.Date %}
<time datetime="{{ post.Date|atom_date }}">
{{ post.Date|date_format:"Jan 2, 2006" }}
</time>
{% endif %}
{% if post.Extra.reading_time %}
<span>{{ post.Extra.reading_time }}</span>
{% endif %}
</footer>
</article>
Example Partial: Navigation ¶ #
{# templates/partials/nav.html #}
<nav class="main-nav">
<a href="/" class="logo">{{ config.Title }}</a>
<ul>
<li><a href="/blog/">Blog</a></li>
<li><a href="/tags/">Tags</a></li>
<li><a href="/about/">About</a></li>
</ul>
</nav>
Post vs Feed Templates ¶ #
Post Templates ¶ #
Post templates render individual content pages. They receive the post and body variables.
Default template: post.html
{# templates/post.html #}
{% extends "base.html" %}
{% block title %}{{ post.Title }} | {{ config.Title }}{% endblock %}
{% block content %}
<article class="post">
<header>
<h1>{{ post.Title }}</h1>
{% if post.Date %}
<time datetime="{{ post.Date|atom_date }}">
{{ post.Date|date_format:"January 2, 2006" }}
</time>
{% endif %}
{% if post.Tags %}
<ul class="tags">
{% for tag in post.Tags %}
<li><a href="/tags/{{ tag|slugify }}/">{{ tag }}</a></li>
{% endfor %}
</ul>
{% endif %}
</header>
<div class="content">
{{ body|safe }}
</div>
</article>
{% endblock %}
Feed Templates ¶ #
Feed templates render lists/archives of posts. They receive feed, page, and posts variables.
Default template: feed.html
{# templates/feed.html #}
{% extends "base.html" %}
{% block title %}{{ feed.Title }} | {{ config.Title }}{% endblock %}
{% block content %}
<section class="feed">
<h1>{{ feed.Title }}</h1>
{% if feed.Description %}
<p class="description">{{ feed.Description }}</p>
{% endif %}
<ul class="post-list">
{% for post in posts %}
<li>
{% include "partials/card.html" %}
</li>
{% endfor %}
</ul>
{% if page.HasPrev or page.HasNext %}
<nav class="pagination">
{% if page.HasPrev %}
<a href="{{ page.PrevURL }}" class="prev">← Previous</a>
{% endif %}
<span>Page {{ page.Number }}</span>
{% if page.HasNext %}
<a href="{{ page.NextURL }}" class="next">Next →</a>
{% endif %}
</nav>
{% endif %}
</section>
{% endblock %}
Custom Templates Per Post ¶ #
Override the default template for specific posts using the template frontmatter field.
Simple Override ¶ #
---
title: "My Landing Page"
template: "landing.html"
---
This post will use templates/landing.html instead of the default post.html.
Creating a Landing Page Template ¶ #
{# templates/landing.html #}
{% extends "base.html" %}
{% block title %}{{ post.Title }}{% endblock %}
{% block header %}
{# No header on landing page #}
{% endblock %}
{% block content %}
<div class="landing">
<section class="hero">
<h1>{{ post.Title }}</h1>
{% if post.Description %}
<p class="tagline">{{ post.Description }}</p>
{% endif %}
</section>
<section class="content">
{{ body|safe }}
</section>
{% if post.Extra.cta_text %}
<section class="cta">
<a href="{{ post.Extra.cta_url }}" class="button">
{{ post.Extra.cta_text }}
</a>
</section>
{% endif %}
</div>
{% endblock %}
{% block footer %}
{# Minimal footer #}
<footer class="minimal">
<p>© {{ config.Author }}</p>
</footer>
{% endblock %}
Template Fallback ¶ #
If a specified template doesn’t exist, markata-go falls back to:
post.html(for posts)feed.html(for feeds)
Complete Examples ¶ #
base.html ¶ #
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}{{ config.Title }}{% endblock %}</title>
<meta name="description" content="{% block description %}{{ config.Description }}{% endblock %}">
{# Open Graph #}
<meta property="og:title" content="{% block og_title %}{{ config.Title }}{% endblock %}">
<meta property="og:description" content="{% block og_description %}{{ config.Description }}{% endblock %}">
<meta property="og:url" content="{{ config.URL }}{% block og_url %}/{% endblock %}">
<meta property="og:site_name" content="{{ config.Title }}">
{# Feeds #}
<link rel="alternate" type="application/rss+xml" title="{{ config.Title }} RSS" href="{{ config.URL }}/blog/rss.xml">
<link rel="alternate" type="application/atom+xml" title="{{ config.Title }} Atom" href="{{ config.URL }}/blog/atom.xml">
{# Styles #}
<link rel="stylesheet" href="/css/style.css">
{% block head %}{% endblock %}
</head>
<body>
<a href="#main" class="skip-link">Skip to content</a>
{% block header %}
<header class="site-header">
<div class="container">
<a href="/" class="logo">{{ config.Title }}</a>
<nav class="main-nav">
<ul>
<li><a href="/blog/">Blog</a></li>
<li><a href="/tags/">Tags</a></li>
<li><a href="/about/">About</a></li>
</ul>
</nav>
</div>
</header>
{% endblock %}
<main id="main">
<div class="container">
{% block content %}{% endblock %}
</div>
</main>
{% block footer %}
<footer class="site-footer">
<div class="container">
<p>© {{ config.Author }}. Built with <a href="https://github.com/example/markata-go">markata-go</a>.</p>
</div>
</footer>
{% endblock %}
{% block scripts %}{% endblock %}
</body>
</html>
post.html ¶ #
{% extends "base.html" %}
{% block title %}{{ post.Title }} | {{ config.Title }}{% endblock %}
{% block description %}{{ post.Description|default_if_none:config.Description }}{% endblock %}
{% block og_title %}{{ post.Title }}{% endblock %}
{% block og_description %}{{ post.Description|default_if_none:config.Description }}{% endblock %}
{% block og_url %}{{ post.Href }}{% endblock %}
{% block head %}
<meta property="og:type" content="article">
{% if post.Date %}
<meta property="article:published_time" content="{{ post.Date|atom_date }}">
{% endif %}
{% if post.Tags %}
{% for tag in post.Tags %}
<meta property="article:tag" content="{{ tag }}">
{% endfor %}
{% endif %}
{# JSON-LD structured data #}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "{{ post.Title }}",
"description": "{{ post.Description }}",
"author": {
"@type": "Person",
"name": "{{ config.Author }}"
},
{% if post.Date %}
"datePublished": "{{ post.Date|atom_date }}",
{% endif %}
"url": "{{ config.URL }}{{ post.Href }}"
}
</script>
{% endblock %}
{% block content %}
<article class="post h-entry">
<header class="post-header">
<h1 class="post-title p-name">{{ post.Title }}</h1>
<div class="post-meta">
{% if post.Date %}
<time class="dt-published" datetime="{{ post.Date|atom_date }}">
{{ post.Date|date_format:"January 2, 2006" }}
</time>
{% endif %}
{% if post.Extra.reading_time %}
<span class="reading-time">{{ post.Extra.reading_time }}</span>
{% endif %}
</div>
{% if post.Tags %}
<ul class="post-tags">
{% for tag in post.Tags %}
<li>
<a href="/tags/{{ tag|slugify }}/" rel="tag" class="p-category">{{ tag }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
</header>
<div class="post-content e-content">
{{ body|safe }}
</div>
<footer class="post-footer">
<div class="author-info">
<span>Written by</span>
<a href="/about/" class="p-author h-card" rel="author">{{ config.Author }}</a>
</div>
{% if post.Extra.prev or post.Extra.next %}
<nav class="post-nav">
{% if post.Extra.prev %}
<a href="{{ post.Extra.prev.Href }}" class="prev" rel="prev">
<span>← Previous</span>
<span class="title">{{ post.Extra.prev.Title }}</span>
</a>
{% endif %}
{% if post.Extra.next %}
<a href="{{ post.Extra.next.Href }}" class="next" rel="next">
<span>Next →</span>
<span class="title">{{ post.Extra.next.Title }}</span>
</a>
{% endif %}
</nav>
{% endif %}
</footer>
</article>
{% endblock %}
feed.html ¶ #
{% extends "base.html" %}
{% block title %}{{ feed.Title }} | {{ config.Title }}{% endblock %}
{% block description %}{{ feed.Description|default_if_none:config.Description }}{% endblock %}
{% block og_title %}{{ feed.Title }}{% endblock %}
{% block og_url %}/{{ feed.Slug }}/{% endblock %}
{% block head %}
<meta property="og:type" content="website">
{% endblock %}
{% block content %}
<section class="feed">
<header class="feed-header">
<h1>{{ feed.Title }}</h1>
{% if feed.Description %}
<p class="feed-description">{{ feed.Description }}</p>
{% endif %}
</header>
{% if posts|length > 0 %}
<ul class="post-list">
{% for post in posts %}
<li class="post-item">
<article class="card h-entry">
<h2 class="card-title">
<a href="{{ post.href }}" class="p-name u-url">{{ post.title }}</a>
</h2>
{% if post.description %}
<p class="card-description p-summary">{{ post.description }}</p>
{% endif %}
<footer class="card-meta">
{% if post.date %}
<time class="dt-published" datetime="{{ post.date|atom_date }}">
{{ post.date|date_format:"Jan 2, 2006" }}
</time>
{% endif %}
{% if post.tags %}
<ul class="card-tags">
{% for tag in post.tags %}
<li><a href="/tags/{{ tag|slugify }}/">{{ tag }}</a></li>
{% endfor %}
</ul>
{% endif %}
</footer>
</article>
</li>
{% endfor %}
</ul>
{% if page.HasPrev or page.HasNext %}
<nav class="pagination" aria-label="Pagination">
{% if page.HasPrev %}
<a href="{{ page.PrevURL }}" class="pagination-prev" rel="prev">
← Newer Posts
</a>
{% else %}
<span class="pagination-prev disabled">← Newer Posts</span>
{% endif %}
<span class="pagination-info">Page {{ page.Number }}</span>
{% if page.HasNext %}
<a href="{{ page.NextURL }}" class="pagination-next" rel="next">
Older Posts →
</a>
{% else %}
<span class="pagination-next disabled">Older Posts →</span>
{% endif %}
</nav>
{% endif %}
{% else %}
<p class="no-posts">No posts found.</p>
{% endif %}
</section>
{% endblock %}
partials/card.html ¶ #
<article class="card h-entry">
{% if post.cover_image %}
<a href="{{ post.href }}" class="card-image">
<img src="{{ post.cover_image }}" alt="{{ post.title }}" loading="lazy">
</a>
{% endif %}
<div class="card-body">
<h2 class="card-title">
<a href="{{ post.href }}" class="p-name u-url">{{ post.title }}</a>
</h2>
{% if post.description %}
<p class="card-description p-summary">
{{ post.description|truncate:160 }}
</p>
{% endif %}
<footer class="card-footer">
{% if post.date %}
<time class="dt-published" datetime="{{ post.date|atom_date }}">
{{ post.date|date_format:"Jan 2, 2006" }}
</time>
{% endif %}
{% if post.reading_time %}
<span class="reading-time">{{ post.reading_time }}</span>
{% endif %}
{% if post.tags %}
<ul class="card-tags">
{% for tag in post.tags|slice:":3" %}
<li><a href="/tags/{{ tag|slugify }}/" class="p-category">{{ tag }}</a></li>
{% endfor %}
</ul>
{% endif %}
</footer>
</div>
</article>
partials/header.html ¶ #
<header class="site-header">
<div class="container">
<a href="/" class="site-logo">
{% if config.Extra.logo %}
<img src="{{ config.Extra.logo }}" alt="{{ config.Title }}">
{% else %}
{{ config.Title }}
{% endif %}
</a>
<nav class="main-nav" aria-label="Main navigation">
<button class="nav-toggle" aria-expanded="false" aria-controls="nav-menu">
<span class="sr-only">Menu</span>
<span class="hamburger"></span>
</button>
<ul id="nav-menu" class="nav-menu">
<li><a href="/">Home</a></li>
<li><a href="/blog/">Blog</a></li>
<li><a href="/tags/">Tags</a></li>
<li><a href="/about/">About</a></li>
</ul>
</nav>
</div>
</header>
partials/footer.html ¶ #
<footer class="site-footer">
<div class="container">
<div class="footer-content">
<div class="footer-section">
<h3>{{ config.Title }}</h3>
<p>{{ config.Description }}</p>
</div>
<div class="footer-section">
<h3>Links</h3>
<ul>
<li><a href="/blog/">Blog</a></li>
<li><a href="/tags/">Tags</a></li>
<li><a href="/about/">About</a></li>
</ul>
</div>
<div class="footer-section">
<h3>Subscribe</h3>
<ul>
<li><a href="/blog/rss.xml">RSS Feed</a></li>
<li><a href="/blog/atom.xml">Atom Feed</a></li>
<li><a href="/blog/feed.json">JSON Feed</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>© {{ config.Author }}. Built with <a href="https://github.com/example/markata-go">markata-go</a>.</p>
</div>
</div>
</footer>
Microformats2 Support ¶ #
markata-go’s default templates include Microformats2 markup for better interoperability with IndieWeb tools and feed readers.
h-entry on Posts ¶ #
Single post pages include h-entry markup on the <article> element:
<article class="post h-entry">
<a class="u-url" href="https://example.com/my-post/" hidden></a>
<header>
<h1 class="p-name">Post Title</h1>
<time class="dt-published" datetime="2024-01-15T00:00:00Z">January 15, 2024</time>
<span class="p-author h-card" hidden>
<a class="u-url p-name" href="https://example.com">Author Name</a>
</span>
</header>
<div class="post-content e-content">
<!-- rendered content -->
</div>
<div class="tags">
<a class="p-category" href="/tags/topic/">topic</a>
</div>
</article>
Properties used:
h-entry- Entry/post containerp-name- Post titleu-url- Canonical permalinkdt-published- Publication datee-content- Entry content (HTML)p-summary- Post description/excerptp-category- Tags/categoriesp-author h-card- Author information
h-feed on Listings ¶ #
Feed and listing pages include h-feed markup:
<div class="feed h-feed">
<h1 class="p-name">Blog Posts</h1>
<p class="p-summary">All blog posts</p>
<span class="p-author h-card" hidden>
<a class="u-url p-name" href="https://example.com">Site Author</a>
</span>
<!-- Each card is an h-entry -->
<article class="card h-entry">...</article>
</div>
Card Types ¶ #
All card templates include appropriate microformat classes:
| Card Type | Key Classes |
|---|---|
| article-card | h-entry, p-name, u-url, p-summary, dt-published, p-category |
| note-card | h-entry, p-content, dt-published |
| photo-card | h-entry, u-photo, u-video, p-name, p-summary |
| video-card | h-entry, u-photo (thumbnail), p-name, p-summary |
| link-card | h-entry, u-bookmark-of, p-name, p-summary |
| quote-card | h-entry, e-content, h-cite |
| guide-card | h-entry, p-name, u-url, p-summary |
| inline-card | h-entry, e-content |
Validation ¶ #
Test your microformats with:
- IndieWebify.me - Validate h-entry markup
- Pin13 - Parse and view microformats
Text Templates ¶ #
markata-go generates .txt versions of posts for consumers that need plain text (LLMs, CLI tools, accessibility readers, etc.). Text templates require special handling because pongo2 auto-escapes all {{ }} output by default, which would produce HTML entities (&, <) in plain text files.
The plaintext Filter ¶ #
Use |plaintext for any variable that contains HTML content (like rendered Markdown):
{{ post.content|plaintext }}
This filter:
- Strips all HTML tags
- Decodes HTML entities to literal characters
- Converts
<a href="...">links to footnote-style references - Marks the output as safe so pongo2 won’t re-escape it
The safe Filter for Metadata ¶ #
For plain text strings that never contain HTML (like title and description), use |safe to prevent auto-escaping:
{{ post.title|safe }}
{{ post.description|safe }}
Example: default.txt ¶ #
{{ post.title|safe }}
{{ post.description|safe }}
{{ post.content|plaintext }}
Link Footnotes ¶ #
Links in HTML content are converted to numbered footnote references:
Read the Go docs [1] for more info. See also the
Go blog [2] for tutorials.
References:
[1]: https://go.dev/doc/
[2]: https://go.dev/blog/
Duplicate URLs reuse the same reference number. When link text is the same as the URL, no footnote marker is added.
Tips and Best Practices ¶ #
-
Always use
|safefor HTML content - When outputting rendered HTML (likebodyorpost.ArticleHTML), use thesafefilter to prevent double-escaping. -
Provide fallbacks with
default_if_none- Use fallback values for optional fields to avoid empty output. -
Use template inheritance - Create a solid
base.htmland extend it for consistency across your site. -
Keep partials small and focused - Each partial should do one thing well.
-
Use semantic HTML - Include appropriate ARIA attributes and semantic elements for accessibility.
-
Add microformats - Include h-entry, h-card, and other microformats for better interoperability.
-
Cache considerations - Templates are cached for performance. Clear the cache during development if changes don’t appear.
See Also ¶ #
- [[configuration-guide|Configuration Guide]] - Configure template settings
- [[feeds-guide|Feeds Guide]] - Learn about the feed system
- [[themes-and-styling|Themes Guide]] - Create and customize themes
- pongo2 Documentation - Full template engine reference