aboutsummaryrefslogtreecommitdiff
path: root/content/templates
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-08-11 14:28:56 -0500
committerChristian Cleberg <[email protected]>2026-08-11 20:35:15 -0500
commitb6870928c46c1eec7103746bd3153a671b8d344c (patch)
tree3949563cc66333bd8745a62a2fe878566789cbdd /content/templates
parent4d78517a19684cc574de3754f6f36c82f0a8179e (diff)
downloadcleberg.net-b6870928c46c1eec7103746bd3153a671b8d344c.tar.gz
cleberg.net-b6870928c46c1eec7103746bd3153a671b8d344c.tar.bz2
cleberg.net-b6870928c46c1eec7103746bd3153a671b8d344c.zip
Render the table of contents, and group /blog/ by year
Two of the differences the page-by-page diff against the live site turned up. The TOC was the larger one: live has one on 171 pages and the org-ssg build had none. `[html] toc = true` only *exposes* `page.toc` — rendering it is the layout's job, and base.html never did. It now emits the same `div#table-of-contents` Emacs does, from a recursive macro over the tree. The anchors already matched, so every link lands where the live one does. Pages that say `#+OPTIONS: toc:nil` get an empty tree and render nothing, which is why /uses/, /now/ and /salary/ have no TOC here either — the same three as live. /blog/ now breaks its list under year headings, as the live index does, using `page.year` and minijinja's groupby. `group_by_year` at the top of blog.html turns it off for one flat list. After both, /blog/ matches the live page's content exactly, and 171 of 171 TOCs are accounted for.
Diffstat (limited to 'content/templates')
-rw-r--r--content/templates/base.html25
-rw-r--r--content/templates/blog.html18
2 files changed, 42 insertions, 1 deletions
diff --git a/content/templates/base.html b/content/templates/base.html
index 6345700..cfda0f1 100644
--- a/content/templates/base.html
+++ b/content/templates/base.html
@@ -10,6 +10,27 @@
Available here: page (.title .url .source .date .date_iso .tags .excerpt .toc
.keywords), site, nav, root, stylesheet. See the org-ssg guide on Templates. #}
+{#- The table of contents, in the shape Emacs exports it. `page.toc` is a tree of
+ {title, anchor, level, children}, and it is empty when the page has no headings or
+ says `#+OPTIONS: toc:nil` — so pages that opt out simply render nothing here. #}
+{%- macro toc_list(entries) -%}
+<ul>
+{%- for e in entries %}
+<li><a href="#{{ e.anchor }}">{{ e.title }}</a>
+{%- if e.children %}{{ toc_list(e.children) }}{% endif %}</li>
+{%- endfor %}
+</ul>
+{%- endmacro %}
+{%- macro toc(entries) -%}
+{%- if entries %}
+<div id="table-of-contents" role="doc-toc">
+<h2>Table of Contents</h2>
+<div id="text-table-of-contents" role="doc-toc">
+{{- toc_list(entries) }}
+</div>
+</div>
+{%- endif %}
+{%- endmacro %}
<!DOCTYPE html>
<html lang="{{ site.language }}">
<head>
@@ -59,7 +80,9 @@
{%- endif %}
{#- Blog posts add a reply footer inside this block; see post.html, which the
[[pages]] rule in org-ssg.toml selects for everything under blog/. #}
-<div>{% block content %}{{ body | safe }}{% endblock %}</div>
+<div>
+{{- toc(page.toc) }}
+{% block content %}{{ body | safe }}{% endblock %}</div>
</article>
{% endblock %}
</main>
diff --git a/content/templates/blog.html b/content/templates/blog.html
index ad6720a..17f8870 100644
--- a/content/templates/blog.html
+++ b/content/templates/blog.html
@@ -1,16 +1,34 @@
{# The blog index at /blog/, mirroring theme/templates/blog.html.
Separate from list.html because the live page carries its own prose. #}
{% extends "base.html" %}
+
+{#- Group the list under year headings, as the live site does. Set to false for one flat
+ list; styles.css has .post-list-year either way. `page.year` comes from #+DATE:, and
+ a post without one lands in its own group at the end rather than being dropped. #}
+{%- set group_by_year = true %}
+
{% block main %}
<h1>{{ page.title }}</h1>
<p>Use <code>&#8984; F</code> / <code>Ctrl F</code> to search &middot; <a href="{{ root }}feed.xml">RSS Feed</a></p>
<p>Want to browse by topic instead? Head over to the <a href="{{ root }}tags/index.html">tags</a> page.</p>
<ul class="post-list">
+{%- if group_by_year %}
+{%- for year, posts in pages | groupby("year") | reverse %}
+<li class="post-list-year">{{ year if year else "undated" }}</li>
+{%- for entry in posts %}
+<li class="post-list-item">
+{%- if entry.date_iso %}<time datetime="{{ entry.date_iso }}">{{ entry.date_iso }}</time>{% endif %}
+<a href="{{ root }}{{ entry.url }}">{{ entry.title }}</a>
+</li>
+{%- endfor %}
+{%- endfor %}
+{%- else %}
{%- for entry in pages %}
<li class="post-list-item">
{%- if entry.date_iso %}<time datetime="{{ entry.date_iso }}">{{ entry.date_iso }}</time>{% endif %}
<a href="{{ root }}{{ entry.url }}">{{ entry.title }}</a>
</li>
{%- endfor %}
+{%- endif %}
</ul>
{% endblock %}