From a4e35942fee745e64db5d0099ba2582e0fb4417e Mon Sep 17 00:00:00 2001
From: Dennis Fink
Date: Thu, 18 Jun 2026 17:57:51 +0200
Subject: Build the first usable version of the blog
This adds the Lektor project structure, models, templates, styling,
feeds, categories, tags, archives, custom Markdown heading handling, and
the initial content needed to make the blog usable.
---
templates/.djlintrc | 14 +++
templates/about.html | 11 ++
templates/base.html | 167 +++++++++++++++++++++++++++++
templates/blog-month-index.html | 54 ++++++++++
templates/blog-post.html | 31 ++++++
templates/blog-year-index.html | 54 ++++++++++
templates/blog.html | 15 +++
templates/categories.html | 53 ++++++++++
templates/category.html | 51 +++++++++
templates/index.html | 15 +++
templates/macros/blog.html | 100 ++++++++++++++++++
templates/macros/pagination.html | 221 +++++++++++++++++++++++++++++++++++++++
templates/page.html | 3 +
templates/tag.html | 59 +++++++++++
14 files changed, 848 insertions(+)
create mode 100644 templates/.djlintrc
create mode 100644 templates/about.html
create mode 100644 templates/base.html
create mode 100644 templates/blog-month-index.html
create mode 100644 templates/blog-post.html
create mode 100644 templates/blog-year-index.html
create mode 100644 templates/blog.html
create mode 100644 templates/categories.html
create mode 100644 templates/category.html
create mode 100644 templates/index.html
create mode 100644 templates/macros/blog.html
create mode 100644 templates/macros/pagination.html
create mode 100644 templates/page.html
create mode 100644 templates/tag.html
(limited to 'templates')
diff --git a/templates/.djlintrc b/templates/.djlintrc
new file mode 100644
index 0000000..5882faa
--- /dev/null
+++ b/templates/.djlintrc
@@ -0,0 +1,14 @@
+{
+"blank_line_before_tag": "macro",
+"blank_line_after_tag": "endmacro,extends,import,from",
+"profile": "jinja",
+"format_css": true,
+"css": {
+"indent_size": 2
+},
+"format_js": true,
+"js": {
+"indent_size": 2
+},
+"indent": "2"
+}
diff --git a/templates/about.html b/templates/about.html
new file mode 100644
index 0000000..55a0778
--- /dev/null
+++ b/templates/about.html
@@ -0,0 +1,11 @@
+{% extends "base.html" %}
+
+{% block meta %}
+ {{ super() }}
+
+
+
+{% endblock %}
+{% block content %}
+ {{ this.body }}
+{% endblock %}
diff --git a/templates/base.html b/templates/base.html
new file mode 100644
index 0000000..1000014
--- /dev/null
+++ b/templates/base.html
@@ -0,0 +1,167 @@
+
+
+
+ {% block head %}
+ {% block meta %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% endblock meta %}
+ {% block styles %}
+
+
+
+ {% endblock styles %}
+ {% block meta_links %}
+
+
+
+
+ {% endblock meta_links %}
+
+ {% block meta_title %}
+ {%- if (this.title or title) -%}{{ (this.title or title) | safe }} —
+ {% endif -%}
+ {{ bag("blog.name") }} — dennisfink.me
+ {% endblock meta_title %}
+
+ {% endblock head %}
+
+
+ {% block body %}
+ Skip to main content
+
+ {% block site_header %}
+
+
+
+ {{ bag("blog.tagline") }}
+
+
+
+
+ {% endblock site_header %}
+
+
+ {% block content required %}
+ {% endblock content %}
+
+
+ {% endblock body %}
+
+
diff --git a/templates/blog-month-index.html b/templates/blog-month-index.html
new file mode 100644
index 0000000..d114628
--- /dev/null
+++ b/templates/blog-month-index.html
@@ -0,0 +1,54 @@
+{% extends "base.html" %}
+
+{% from "macros/pagination.html" import render_month_archive_navigation %}
+
+{% block meta %}
+ {{ super() }}
+
+{% endblock %}
+{% block styles %}
+ {{ super() }}
+
+{% endblock %}
+{% set title = "Monthly archive: " + this.date | dateformat('MMMM y') %}
+{% block content %}
+ {% set post_count = this.children.count() %}
+
+
+
+
+
+ | Date |
+ Title |
+
+
+
+ {% for post in this.children.order_by('-publication_date') %}
+
+ |
+
+ |
+
+ {{ post.title }}
+ |
+
+ {% endfor %}
+
+
+ {{ render_month_archive_navigation(this) }}
+
+{% endblock %}
diff --git a/templates/blog-post.html b/templates/blog-post.html
new file mode 100644
index 0000000..6ffb600
--- /dev/null
+++ b/templates/blog-post.html
@@ -0,0 +1,31 @@
+{% extends "base.html" %}
+
+{% from "macros/blog.html" import render_blog_post %}
+
+{% block meta %}
+ {{ super() }}
+
+
+ {% if this.publication_date %}
+
+ {% endif %}
+ {% if this.updated_date %}
+
+ {% endif %}
+ {% if this.category %}
+ {% set category = site.get("/categories/" + this.category) %}
+
+ {% endif %}
+ {% for t in this.tags %}
+
+ {% endfor %}
+{% endblock %}
+{% block styles %}
+ {{ super() }}
+
+{% endblock %}
+{% block content %}{{ render_blog_post(this) }}{% endblock %}
diff --git a/templates/blog-year-index.html b/templates/blog-year-index.html
new file mode 100644
index 0000000..138cc86
--- /dev/null
+++ b/templates/blog-year-index.html
@@ -0,0 +1,54 @@
+{% extends "base.html" %}
+
+{% from "macros/pagination.html" import render_year_archive_navigation %}
+
+{% block meta %}
+ {{ super() }}
+
+{% endblock %}
+{% block styles %}
+ {{ super() }}
+
+{% endblock %}
+{% set title = "Yearly archive: " + this.key %}
+{% block content %}
+ {% set post_count = this.children.count() %}
+
+
+
+
+
+ | Date |
+ Title |
+
+
+
+ {% for post in this.children.order_by('-publication_date') %}
+
+ |
+
+ |
+
+ {{ post.title }}
+ |
+
+ {% endfor %}
+
+
+ {{ render_year_archive_navigation(this) }}
+
+{% endblock %}
diff --git a/templates/blog.html b/templates/blog.html
new file mode 100644
index 0000000..0ee96c7
--- /dev/null
+++ b/templates/blog.html
@@ -0,0 +1,15 @@
+{% extends "base.html" %}
+
+{% from "macros/blog.html" import render_blog_index %}
+
+{% block meta %}
+ {{ super() }}
+
+{% endblock %}
+{% block styles %}
+ {{ super() }}
+
+{% endblock %}
+{% block content %}{{ render_blog_index(this.pagination) }}{% endblock %}
diff --git a/templates/categories.html b/templates/categories.html
new file mode 100644
index 0000000..4dbdf1a
--- /dev/null
+++ b/templates/categories.html
@@ -0,0 +1,53 @@
+{% extends "base.html" %}
+
+{% block meta %}
+ {{ super() }}
+
+{% endblock %}
+{% block styles %}
+ {{ super() }}
+
+{% endblock %}
+{% set title = "Categories" %}
+{% block content %}
+
+
+
+
+{% endblock %}
diff --git a/templates/category.html b/templates/category.html
new file mode 100644
index 0000000..9d6b35d
--- /dev/null
+++ b/templates/category.html
@@ -0,0 +1,51 @@
+{% extends "base.html" %}
+
+{% block meta %}
+ {{ super() }}
+
+{% endblock %}
+{% block styles %}
+ {{ super() }}
+
+{% endblock %}
+{% set title = "Category " + this.name %}
+{% block content %}
+ {% set post_count = this.children.count() %}
+
+
+
+
+
+ | Date |
+ Title |
+
+
+
+ {% for post in this.children.order_by('-publication_date') %}
+
+ |
+
+ |
+
+ {{ post.title }}
+ |
+
+ {% endfor %}
+
+
+
+{% endblock %}
diff --git a/templates/index.html b/templates/index.html
new file mode 100644
index 0000000..0ee96c7
--- /dev/null
+++ b/templates/index.html
@@ -0,0 +1,15 @@
+{% extends "base.html" %}
+
+{% from "macros/blog.html" import render_blog_index %}
+
+{% block meta %}
+ {{ super() }}
+
+{% endblock %}
+{% block styles %}
+ {{ super() }}
+
+{% endblock %}
+{% block content %}{{ render_blog_index(this.pagination) }}{% endblock %}
diff --git a/templates/macros/blog.html b/templates/macros/blog.html
new file mode 100644
index 0000000..b4caafe
--- /dev/null
+++ b/templates/macros/blog.html
@@ -0,0 +1,100 @@
+{% from "macros/pagination.html" import render_pagination %}
+
+{% macro render_toc(toc, maxdepth=2) %}
+
+{% endmacro %}
+
+{% macro render_blog_post(post, show_toc=True) %}
+
+
+
+
+
+ {% if post.publication_date %}
+
+ {% else %}
+
+
- ⚠
+ -
+ Missing publication date
+
+
+ {% endif %}
+ {% if post.updated_date and post.publication_date and (post.updated_date > post.publication_date) %}
+
+
- Last updated:
+ -
+
+
+
+ {% endif %}
+ {% if post.category %}
+
+
- Category:
+
-
+ {% set category = site.get('/categories/'+post.category) %}
+ {{ category.name }}
+
+
+ {% endif %}
+
+
+ {% if show_toc and post.render_toc %}
+ {% set toc = post.body.toc|default([]) %}
+ {{ render_toc(toc) }}
+ {% endif %}
+
+ {{ post.body }}
+ {% if post.tags %}
+
+ {% endif %}
+
+{% endmacro %}
+
+{% macro render_blog_index(pagination) %}
+ {% for post in pagination.items %}
+ {{ render_blog_post(post, False) }}
+ {% if not loop.last %}
+
+ {% endif %}
+ {% endfor %}
+ {% if pagination.pages > 1 %}
+
+ {% endif %}
+{% endmacro %}
diff --git a/templates/macros/pagination.html b/templates/macros/pagination.html
new file mode 100644
index 0000000..adb1b5c
--- /dev/null
+++ b/templates/macros/pagination.html
@@ -0,0 +1,221 @@
+{% macro render_pagination(pagination, pagination_window=2) %}
+ {% set current = pagination.page %}
+ {% set total = pagination.pages %}
+ {% set window_start = [current - pagination_window, 1] | max %}
+ {% set window_end = [current + pagination_window, total] | min %}
+
+{% endmacro %}
+
+{% macro render_year_archive_navigation(
+ current_year_page,
+ archive_window=2
+ ) %}
+ {% set current_year = current_year_page.year %}
+ {% set first_year_page = current_year_page.first_year_page %}
+ {% set last_year_page = current_year_page.last_year_page %}
+ {% set previous_year_page = current_year_page.previous_year_page %}
+ {% set next_year_page = current_year_page.next_year_page %}
+ {% set first_year = first_year_page.year %}
+ {% set last_year = last_year_page.year %}
+ {% set window_start = [current_year - archive_window, first_year] | max %}
+ {% set window_end = [current_year + archive_window, last_year] | min %}
+
+{% endmacro %}
+
+{% macro render_month_archive_navigation(
+ current_month_page,
+ archive_window=2
+ ) %}
+ {% set year_pages = index_pages('year') | sort(attribute='year') %}
+ {% set ns = namespace(
+ month_pages=[],
+ current_position=none
+ ) %}
+ {% for year_page in year_pages %}
+ {% set ns.month_pages = ns.month_pages + (year_page.subindexes | sort(attribute='month')) %}
+ {% endfor %}
+ {% for month_page in ns.month_pages %}
+ {% if month_page.year == current_month_page.year
+ and month_page.month == current_month_page.month %}
+ {% set ns.current_position = loop.index0 %}
+ {% endif %}
+ {% endfor %}
+ {% set total = ns.month_pages | length %}
+ {% set current_position = ns.current_position %}
+ {% set window_start = [current_position - archive_window, 0] | max %}
+ {% set window_end = [current_position + archive_window, total - 1] | min %}
+ {% set first_month_page = ns.month_pages[0] %}
+ {% set last_month_page = ns.month_pages[total - 1] %}
+ {% set previous_month_page = none %}
+ {% set next_month_page = none %}
+ {% if current_position > 0 %}
+ {% set previous_month_page = ns.month_pages[current_position - 1] %}
+ {% endif %}
+ {% if current_position < total - 1 %}
+ {% set next_month_page = ns.month_pages[current_position + 1] %}
+ {% endif %}
+
+{% endmacro %}
diff --git a/templates/page.html b/templates/page.html
new file mode 100644
index 0000000..dd20955
--- /dev/null
+++ b/templates/page.html
@@ -0,0 +1,3 @@
+{% extends "base.html" %}
+
+{% block content %}{{ this.body }}{% endblock %}
diff --git a/templates/tag.html b/templates/tag.html
new file mode 100644
index 0000000..b433616
--- /dev/null
+++ b/templates/tag.html
@@ -0,0 +1,59 @@
+{% extends "base.html" %}
+
+{% block meta %}
+ {{ super() }}
+
+{% endblock %}
+{% block styles %}
+ {{ super() }}
+
+{% endblock %}
+{% set title = "Tag " + this.tag %}
+{% block content %}
+ {% set post_count = this.items.count() %}
+
+
+
+
+
+ | Date |
+ Title |
+
+
+
+ {% for post in this.items %}
+
+ |
+ {% if post.publication_date %}
+
+ {% endif %}
+ |
+
+ {{ post.title }}
+ |
+
+ {% else %}
+
+ |
+ No posts for this tag.
+ |
+
+ {% endfor %}
+
+
+
+{% endblock %}
--
cgit v1.3.1