diff options
| author | Dennis Fink | 2026-06-18 17:57:51 +0200 |
|---|---|---|
| committer | Dennis Fink | 2026-06-18 17:57:51 +0200 |
| commit | a4e35942fee745e64db5d0099ba2582e0fb4417e (patch) | |
| tree | f109c3e77805cdacd6f443c8a2d47514be5254a2 /templates | |
| parent | e0904e48a5c1c5b327c29218fb39bf2fa05f1871 (diff) | |
| download | dennisfink.me-a4e35942fee745e64db5d0099ba2582e0fb4417e.tar.gz dennisfink.me-a4e35942fee745e64db5d0099ba2582e0fb4417e.zip | |
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.
Diffstat (limited to '')
| -rw-r--r-- | templates/.djlintrc | 14 | ||||
| -rw-r--r-- | templates/about.html | 11 | ||||
| -rw-r--r-- | templates/base.html | 167 | ||||
| -rw-r--r-- | templates/blog-month-index.html | 54 | ||||
| -rw-r--r-- | templates/blog-post.html | 31 | ||||
| -rw-r--r-- | templates/blog-year-index.html | 54 | ||||
| -rw-r--r-- | templates/blog.html | 15 | ||||
| -rw-r--r-- | templates/categories.html | 53 | ||||
| -rw-r--r-- | templates/category.html | 51 | ||||
| -rw-r--r-- | templates/index.html | 15 | ||||
| -rw-r--r-- | templates/macros/blog.html | 100 | ||||
| -rw-r--r-- | templates/macros/pagination.html | 221 | ||||
| -rw-r--r-- | templates/page.html | 3 | ||||
| -rw-r--r-- | templates/tag.html | 59 |
14 files changed, 848 insertions, 0 deletions
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() }} + <meta property="og:type" content="profile"> + <meta property="profile:first_name" content="Dennis"> + <meta property="profile:last_name" content="Fink"> +{% endblock %} +{% block content %} + <article>{{ this.body }}</article> +{% 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 @@ +<!DOCTYPE html> +<html lang="en" + prefix="og: https://ogp.me/ns# article: https://ogp.me/ns/article# profile: https://ogp.me/ns/profile#"> + <head> + {% block head %} + {% block meta %} + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="color-scheme" content="dark light"> + <meta name="generator" content="lektor"> + <meta name="description" + property="og:description" + content="{{ bag('blog.tagline') }}"> + <meta property="og:site_name" content="dennisfink.me"> + <meta property="og:title" + content="{%- if (this.title or title) -%}{{ (this.title or title) | safe }} — {% endif -%}{{ bag('blog.name') }}"> + <meta property="og:url" + content="{{ this | url(absolute=True, external=True) }}"> + <meta property="og:image" + content="{{ '/static/images/opengraph.png' | url(absolute=True, external=True) }}"> + <meta property="og:image:type" content="image/png"> + <meta property="og:image:width" content="32"> + <meta property="og:image:height" content="32"> + <meta property="og:image:alt" + content="Logo of the site showing a second-order Hilbert curve segment drawn as a continuous right-angled line inside a rounded square, with both colored in muted green"> + <meta name="fediverse:creator" content="@me@dennisfink.me"> + {% endblock meta %} + {% block styles %} + <link rel="stylesheet" + type="text/css" + href="{{ '/static/css/modern_normalize.css' | asseturl }}"> + <link rel="stylesheet" + type="text/css" + href="{{ '/static/css/libre_baskerville.css' | asseturl }}"> + <link rel="stylesheet" + type="text/css" + href="{{ '/static/css/style.css' | asseturl }}"> + {% endblock styles %} + {% block meta_links %} + <link rel="canonical" href="{{ this|url(absolute=True, external=True) }}"> + <link rel="alternate" + type="application/atom+xml" + title="{{ bag('blog.name') }} — Latest posts" + href="{{ '/blog@atom/latest'|url(absolute=True) }}"> + <link rel="alternate" + type="application/atom+xml" + title="{{ bag('blog.name') }} — All posts" + href="{{ '/blog@atom/all'|url(absolute=True) }}"> + <link rel="shortcut icon" + type="image/png" + href="{{ '/static/images/favicon.png' | asseturl }}"> + {% endblock meta_links %} + <title> + {% block meta_title %} + {%- if (this.title or title) -%}{{ (this.title or title) | safe }} — + {% endif -%} + {{ bag("blog.name") }} — dennisfink.me + {% endblock meta_title %} + </title> + {% endblock head %} + </head> + <body> + {% block body %} + <a href="#main" class="skip-link">Skip to main content</a> + <header> + {% block site_header %} + <div> + <hgroup> + <h1> + <a href="/">{{ bag("blog.name") }}</a> + </h1> + <p>{{ bag("blog.tagline") }}</p> + </hgroup> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800"> + <path d="M89.4 0C40.1 0 0 40 0 89.4v621.2C0 759.9 40 800 89.4 800h621.2c49.3 0 89.4-40 89.4-89.4V89.4C800 40.1 760 0 710.6 0zm0 18.9h621.2A70 70 0 0 1 781 89.4v621.2a70 70 0 0 1-70.5 70.5H89.4A70 70 0 0 1 19 710.6V89.4A70 70 0 0 1 89.4 19m147.8 71c-11.9-.1-24.6 0-37.2 0-25.4.4-50.7 1.3-70.1 2.7-9.7.7-18 1.5-24 2.5q-4.5.7-7.5 1.7c-2 .6-3.5 1-4.6 3q-.6 1.4-.7 2.9-.3 1.6-.5 4.2l-.9 13.7c-.5 11.4-1 27-1.3 45.5a8909 8909 0 0 0-.2 267.8 1985 1985 0 0 0 2 59.2l.4 4.2q.1 1.5.7 2.8c1 2 2.5 2.4 4.3 3q2.7 1 6.9 1.8 8.4 1.6 22 2.7a1491 1491 0 0 0 64 3.6c23 1 46.2 1.7 63.8 2.5a614 614 0 0 1 28 2l1.4.2.3 1.2.7 5.6c.5 4.8.9 11.6 1.2 19.5a1632 1632 0 0 1 .1 115.5 390 390 0 0 1-1.8 25.3l-.3 1q-.4.3-1.3.4l-6 .6q-7.7.7-20.5 1.1c-16.8.7-38.9 1.1-60.9 1.4a7900 7900 0 0 1-103 .8l-2 .3c-.4 0-1 0-1.6 1.3q-.5.9-.3.8v1.1L88 700l.2 9.3.3.8c.6 1.4 1.2 1.2 1.5 1.3l.7.2 1.4.1 3.7.2h9.1c2.7 0 51.2 0 99.7-.6 24.3-.4 48.5-.9 67-1.7a485 485 0 0 0 30-2.2l2.2-.5c.7-.3 1.3-.3 2.1-1.2q1-1.1 1-2 .4-1.2.6-2.5.5-2.8.8-7.3.8-9.2 1.3-23.9a2277 2277 0 0 0 .1-140 547 547 0 0 0-2-31.2l-.5-2.4c-.2-.7-.2-1.2-1-2-.8-1-1.4-1-2-1.2q-1-.4-2.3-.6l-6.7-.9q-8.4-.7-21.8-1.5c-17.7-1-40.8-1.7-64-2.7-23-.9-46.2-2-63.8-3.4-8.7-.7-16.2-1.5-21.4-2.4q-4-.7-6.2-1.4-1.2-.4-1.5-.7l-.1-.8-.4-3.5q-.5-4.6-.8-12.3c-.4-8-.6-20.1-1-32.3a4578 4578 0 0 1 1.4-306l.5-4.4 1.3-.6q1.8-.8 5.4-1.4 7.4-1.3 19.4-2.3a1269 1269 0 0 1 115.5-1.8 355 355 0 0 1 25.1 1.8l1.2.3.3 1.4.6 6.3q.8 8.2 1.2 21.6c.7 17.7 1.3 40.8 2 64 .6 23 1.2 46.2 2 63.9a465 465 0 0 0 2.4 28.5q.2 1.3.6 2.3.1.9 1.1 2c1 1.1 1.4.8 2.1 1l2.4.5 7.3.8q9.2.7 23.9 1.2a2305 2305 0 0 0 163.9-1.2 101 101 0 0 0 9.7-1.3c.7-.2 1.2-.2 2-1 1-.8 1-1.4 1.2-2q.4-1 .6-2.3l.9-6.7q.8-8.4 1.4-21.8c.9-17.7 1.5-40.9 2.1-64s1.3-46.2 2-63.9a571 571 0 0 1 1.8-27.9l.3-1.4 1.2-.3 5.6-.7a355 355 0 0 1 19.5-1 1269 1269 0 0 1 115.5 1.7q12.1 1 19.4 2.3 3.5.7 5.4 1.4l1.3.6.2.9.3 3.5q.4 4.5.7 12.3a4578 4578 0 0 1-1 338.3l-.6 4.3q-.3.3-1.5.7-2.1.8-6.2 1.4c-5.2 1-12.7 1.7-21.4 2.4-17.6 1.4-40.7 2.5-63.9 3.4-23 1-46.2 1.8-63.9 2.7a493 493 0 0 0-28.5 2.4q-1.3.2-2.3.6c-.6.2-1.2.3-2 1.1s-.8 1.4-1 2.1l-.5 2.4-.8 7.3q-.7 9.2-1.2 23.9a2277 2277 0 0 0 1.4 163.9 97 97 0 0 0 1.3 9.7q.1 1 1 2.1c.9.9 1.5 1 2.2 1.2l2.3.5 7 .9q8.8.8 22.9 1.3c18.5.8 42.7 1.3 67 1.7 48.5.7 97 .6 99.7.6l14.2-.3.7-.2c.3 0 .9 0 1.5-1.3q.5-.9.3-.8v-1.1l.2-8.2-.2-9.3-.3-.8c-.6-1.4-1.2-1.2-1.5-1.3l-.7-.2-1.3-.1-3.7-.2h-8.7c-2.5 0-46.6 0-90.7-.6-22-.3-44-.7-60.9-1.4a483 483 0 0 1-26.5-1.7l-1.3-.3-.3-1.1-.7-5.7a390 390 0 0 1-1-19.5 1632 1632 0 0 1 1.2-135l1-6.9 1.4-.3 6.4-.6 21.6-1.3c17.6-.8 40.7-1.6 63.9-2.5 23-1 46.2-2 64-3.6q13.4-1.2 22-2.7 4-.7 6.8-1.7c1.8-.7 3.4-1.2 4.3-3.1q.6-1.3.7-2.8l.4-4.2q.5-5.1.8-13.7c.5-11.4.8-27 1.2-45.5a8229 8229 0 0 0-1.5-313.3 331 331 0 0 0-1.4-18q0-1.3-.7-2.7c-1-2-2.7-2.5-4.6-3q-3-1-7.5-1.8c-6-1-14.3-1.8-24-2.5a1786 1786 0 0 0-140-2.2 517 517 0 0 0-31.2 1.8l-2.4.5c-.7.2-1.2.2-2 1-1 .8-1 1.4-1.2 2q-.4 1-.6 2.3l-.9 6.7q-.8 8.4-1.4 21.8c-.9 17.7-1.5 40.9-2.1 64s-1.3 46.2-2 63.9a571 571 0 0 1-1.8 27.9l-.3 1.4-1.2.3-5.6.7c-4.8.5-11.6.9-19.5 1.2a1575 1575 0 0 1-135-1.2l-6.9-1-.3-1.4-.6-6.3q-.7-8.2-1.2-21.6c-.7-17.7-1.3-40.8-2-64-.6-23-1.2-46.2-2-63.9a465 465 0 0 0-2.4-28.5q-.2-1.3-.6-2.3-.1-.8-1.1-2c-1-1.2-1.4-.8-2.1-1l-2.4-.5-7.3-.8q-9.2-.7-23.9-1t-32.8-.6M96 147.4c-3.2 65-3.2 230.9-.4 300.6l-.4-14.3a8224 8224 0 0 1 .2-267.6zm608 0 .6 18.7a8887 8887 0 0 1 .2 267.6l-.4 14.3a5034 5034 0 0 0-.4-300.6m-380.2 157c36.4 2.6 116 2.6 152.4 0-2.3 0-3.7.2-6.3.3a2302 2302 0 0 1-139.8 0zm-19.4 218.7.4 7a2273 2273 0 0 1-.1 139.7c-.1 3.3-.3 5.2-.5 8 3-35.2 3-118.2.2-154.7m191.2 0c-2.8 36.5-2.8 119.5.2 154.8-.2-3-.4-4.8-.5-8.1a2274 2274 0 0 1-.1-139.7z" /> + </svg> + </div> + <div> + <nav aria-label="Site navigation"> + {% block site_navigation %} + <ul> + {% block site_navigation_items %} + <li> + <a href="{{ '/blog' | url(absolute=True) }}" + {% if "/blog@" in this.path %}aria-current="page"{% endif %}>Blog</a> + </li> + <li> + <a href="{{ '/categories' | url(absolute=True) }}" + {% if this.path == "/categories" %}aria-current="page"{% endif %}>Categories</a> + </li> + <li> + <a href="{{ '/about' | url(absolute=True) }}" + {% if this.path == "/about" %}aria-current="page"{% endif %}>About</a> + </li> + {% endblock site_navigation_items %} + </ul> + {% endblock site_navigation %} + </nav> + <fieldset class="theme-switcher"> + <legend class="visually-hidden">Color theme</legend> + <label title="Light mode"> + <input type="radio" + name="theme-switcher" + value="light" + id="lightmode" + aria-label="Light Mode"> + <span aria-hidden="true"></span> + </label> + <label title="Auto mode"> + <input type="radio" + name="theme-switcher" + value="light dark" + id="automode" + aria-label="Auto Mode" + checked> + <span aria-hidden="true"></span> + </label> + <label title="Dark mode"> + <input type="radio" + name="theme-switcher" + value="dark" + id="darkmode" + aria-label="Dark Mode"> + <span aria-hidden="true"></span> + </label> + </fieldset> + </div> + {% endblock site_header %} + </header> + <main> + {% block content required %} + {% endblock content %} + </main> + <footer> + {% block site_footer %} + <section aria-label="Follow me"> + <h2>Follow me:</h2> + <div> + <a href="{{ '/blog@atom/latest' | url(absolute=True) }}" + id="atom" + aria-label="Atom feed — Latest posts"></a> + <a rel="nofollow" + href="/email/" + id="email" + aria-label="Mail address: me plus blog at dennisfink.me"></a> + <a rel="me" + href="https://social.dennisfink.me/@me" + id="mastodon" + aria-label="Mastodon profile: me on social.dennisfink.me"></a> + <a href="xmpp:me@dennisfink.me" + id="xmpp" + aria-label="XMPP address: me on dennisfink.me"></a> + </div> + </section> + <section> + <h2>Blogroll</h2> + <ul> + <li> + <a href="https://blog.janguth.eu/">Broadsheet (Jan Guth)</a> + </li> + </ul> + </section> + {% endblock %} + </footer> + {% endblock body %} + </body> +</html> 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() }} + <meta property="og:type" content="website"> +{% endblock %} +{% block styles %} + {{ super() }} + <link rel="stylesheet" + type="text/css" + href="{{ '/static/css/category.css'|asseturl }}"> +{% endblock %} +{% set title = "Monthly archive: " + this.date | dateformat('MMMM y') %} +{% block content %} + {% set post_count = this.children.count() %} + <article> + <header> + <hgroup> + <h2>Monthly archive: {{ this.date | dateformat("MMMM y") }}</h2> + <p> + ({{ post_count }} + {% if post_count > 1 %} + posts) + {% else %} + post) + {% endif %} + </p> + </hgroup> + </header> + <table> + <thead class="visually-hidden"> + <tr> + <th scope="col">Date</th> + <th scope="col">Title</th> + </tr> + </thead> + <tbody> + {% for post in this.children.order_by('-publication_date') %} + <tr> + <td> + <time datetime="{{ post.publication_date }}">{{ post.publication_date }}</time> + </td> + <td> + <a href="{{ post|url }}">{{ post.title }}</a> + </td> + </tr> + {% endfor %} + </tbody> + </table> + {{ render_month_archive_navigation(this) }} + </article> +{% 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() }} + <meta property="og:type" content="article"> + <meta property="article:author" content="https://dennisfink.me/about"> + {% if this.publication_date %} + <meta property="article:published_time" + content="{{ this.publication_date }}T00:00:00Z"> + {% endif %} + {% if this.updated_date %} + <meta property="article:modified_time" + content="{{ this.updated_date }}T00:00:0Z"> + {% endif %} + {% if this.category %} + {% set category = site.get("/categories/" + this.category) %} + <meta property="article:section" content="{{ category.name }}"> + {% endif %} + {% for t in this.tags %} + <meta property="article:tag" content="{{ t }}"> + {% endfor %} +{% endblock %} +{% block styles %} + {{ super() }} + <link rel="stylesheet" + type="text/css" + href="{{ '/static/css/blog.css'|asseturl }}"> +{% 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() }} + <meta property="og:type" content="website"> +{% endblock %} +{% block styles %} + {{ super() }} + <link rel="stylesheet" + type="text/css" + href="{{ '/static/css/category.css'|asseturl }}"> +{% endblock %} +{% set title = "Yearly archive: " + this.key %} +{% block content %} + {% set post_count = this.children.count() %} + <article> + <header> + <hgroup> + <h2>Yearly archive: {{ this.key }}</h2> + <p> + ({{ post_count }} + {% if post_count > 1 %} + posts) + {% else %} + post) + {% endif %} + </p> + </hgroup> + </header> + <table> + <thead class="visually-hidden"> + <tr> + <th scope="col">Date</th> + <th scope="col">Title</th> + </tr> + </thead> + <tbody> + {% for post in this.children.order_by('-publication_date') %} + <tr> + <td> + <time datetime="{{ post.publication_date }}">{{ post.publication_date }}</time> + </td> + <td> + <a href="{{ post|url }}">{{ post.title }}</a> + </td> + </tr> + {% endfor %} + </tbody> + </table> + {{ render_year_archive_navigation(this) }} + </article> +{% 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() }} + <meta property="og:type" content="website"> +{% endblock %} +{% block styles %} + {{ super() }} + <link rel="stylesheet" + type="text/css" + href="{{ '/static/css/blog.css'|asseturl }}"> +{% 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() }} + <meta property="og:type" content="website"> +{% endblock %} +{% block styles %} + {{ super() }} + <link rel="stylesheet" + type="text/css" + href="{{ '/static/css/categories.css'|asseturl }}"> +{% endblock %} +{% set title = "Categories" %} +{% block content %} + <article> + <header> + <h2>Categories</h2> + </header> + <ul> + {% set sorted = [] %} + {% for category in this.children.order_by('name') %} + {% set post_count = category.children.count() %} + {% if post_count > 0 %} + {% set _ = sorted.append((post_count, category)) %} + {% endif %} + {% endfor %} + {% for post_count, category in sorted|sort(attribute='0', reverse=True) %} + {% set last_post = category.children.order_by('-publication_date').first() %} + <li> + <a href="{{ category|url }}"> + <h3>{{ category.name }}</h3> + <dl> + <div> + <dt>Posts:</dt> + <dd> + {{ post_count }} + </dd> + </div> + {% if last_post %} + <div> + <dt>Last post:</dt> + <dd> + <time datetime="{{ last_post.publication_date }}">{{ last_post.publication_date }}</time> + </dd> + </div> + {% endif %} + </dl> + </a> + </li> + {% endfor %} + </ul> + </article> +{% 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() }} + <meta property="og:type" content="website"> +{% endblock %} +{% block styles %} + {{ super() }} + <link rel="stylesheet" + type="text/css" + href="{{ '/static/css/category.css'|asseturl }}"> +{% endblock %} +{% set title = "Category " + this.name %} +{% block content %} + {% set post_count = this.children.count() %} + <article> + <header> + <hgroup> + <h2>Category: {{ this.name }}</h2> + <p> + ({{ post_count }} + {% if post_count > 1 %} + posts) + {% else %} + post) + {% endif %} + </p> + </hgroup> + </header> + <table> + <thead class="visually-hidden"> + <tr> + <th scope="col">Date</th> + <th scope="col">Title</th> + </tr> + </thead> + <tbody> + {% for post in this.children.order_by('-publication_date') %} + <tr> + <td> + <time datetime="{{ post.publication_date }}">{{ post.publication_date }}</time> + </td> + <td> + <a href="{{ post|url }}">{{ post.title }}</a> + </td> + </tr> + {% endfor %} + </tbody> + </table> + </article> +{% 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() }} + <meta property="og:type" content="website"> +{% endblock %} +{% block styles %} + {{ super() }} + <link rel="stylesheet" + type="text/css" + href="{{ '/static/css/blog.css'|asseturl }}"> +{% 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) %} + <nav aria-label="Table of contents"> + <p>Table of contents</p> + <ol> + {% for item in toc recursive %} + <li> + <a href="#{{ item.anchor }}">{{ item.title }}</a> + {% if item.children and loop.depth < maxdepth %} + <ol> + {{ loop(item.children) }} + </ol> + {% endif %} + </li> + {% endfor %} + </ol> + </nav> +{% endmacro %} + +{% macro render_blog_post(post, show_toc=True) %} + <article> + <header> + <div> + <h2> + <a href="{{ post|url }}">{{ post.title }}</a> + </h2> + <dl> + {% if post.publication_date %} + <div> + <dt>Posted:</dt> + <dd> + <a href="{{ ('/blog@index-pages/year/{}'.format(post.publication_date.year) ) | url }}"> + <time datetime="{{ post.publication_date | dateformat('yyyy-MM-dd') }}">{{ post.publication_date | dateformat("d MMMM yyyy") }}</time> + </a> + </dd> + </div> + {% else %} + <div class="missing-publication-date-warning" role="alert"> + <dt>⚠</dt> + <dd> + Missing publication date + </dd> + </div> + {% endif %} + {% if post.updated_date and post.publication_date and (post.updated_date > post.publication_date) %} + <div> + <dt>Last updated:</dt> + <dd> + <time datetime="{{ post.updated_date | dateformat('yyyy-MM-dd') }}">{{ post.updated_date | dateformat("d MMMM yyyy") }}</time> + </dd> + </div> + {% endif %} + {% if post.category %} + <div> + <dt>Category:</dt> + <dd> + {% set category = site.get('/categories/'+post.category) %} + <a href="{{ category|url }}">{{ category.name }}</a> + </dd> + </div> + {% endif %} + </dl> + </div> + {% if show_toc and post.render_toc %} + {% set toc = post.body.toc|default([]) %} + {{ render_toc(toc) }} + {% endif %} + </header> + {{ post.body }} + {% if post.tags %} + <footer> + <p>Tagged with</p> + <ul aria-label="Tags"> + {% for tag in post.tags %} + <li> + {% set tag_slug = tag|trim|lower|replace(" ", "-") %} + <a href="{{ ('/blog/tag/' ~ tag_slug ~ '/')|url }}" rel="tag">{{ tag }}</a> + {%- if not loop.last -%}, {%- else -%}.{%- endif -%} + </li> + {% endfor %} + </ul> + </footer> + {% endif %} + </article> +{% endmacro %} + +{% macro render_blog_index(pagination) %} + {% for post in pagination.items %} + {{ render_blog_post(post, False) }} + {% if not loop.last %} + <hr> + {% endif %} + {% endfor %} + {% if pagination.pages > 1 %} + <footer> + {{ render_pagination(pagination) }} + </footer> + {% 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 %} + <nav aria-label="Page navigation"> + <ol> + <li> + {% if pagination.has_prev %} + <a href="{{ pagination.prev | url }}" aria-label="Previous page">❮</a> + {% else %} + <span aria-disabled="true">❮</span> + {% endif %} + </li> + {% if window_start > 1 %} + <li> + <a href="{{ pagination.for_page(1) | url }}" aria-label="Page 1">1</a> + </li> + {% if window_start > 2 %} + <li> + <span aria-hidden="true">…</span> + </li> + {% endif %} + {% endif %} + {% for page_num in range(window_start, window_end + 1) %} + <li> + {% if page_num == current %} + <a href="{{ pagination.for_page(page_num) | url }}" aria-current="page">{{ page_num }}</a> + {% else %} + <a href="{{ pagination.for_page(page_num) | url }}" + aria-label="Page {{ page_num }}">{{ page_num }}</a> + {% endif %} + </li> + {% endfor %} + {% if window_end < total %} + {% if window_end < total - 1 %} + <li> + <span aria-hidden="true">…</span> + </li> + {% endif %} + <li> + <a href="{{ pagination.for_page(total) | url }}" + aria-label="Page {{ total }}">{{ total }}</a> + </li> + {% endif %} + <li> + {% if pagination.has_next %} + <a href="{{ pagination.next | url }}" aria-label="Next page">❯</a> + {% else %} + <span aria-disabled="true">❯</span> + {% endif %} + </li> + </ol> + </nav> +{% 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 %} + <nav aria-label="Year archive navigation"> + <ol> + <li> + {% if previous_year_page %} + <a href="{{ previous_year_page | url }}" + rel="prev" + aria-label="Previous year: {{ previous_year_page.year }}">❮</a> + {% else %} + <span aria-disabled="true">❮</span> + {% endif %} + </li> + {% if window_start > first_year %} + <li> + <a href="{{ first_year_page | url }}" aria-label="Year {{ first_year }}">{{ first_year }}</a> + </li> + {% if window_start > first_year + 1 %} + <li> + <span aria-hidden="true">…</span> + </li> + {% endif %} + {% endif %} + {% for year in range(window_start, window_end + 1) %} + {% set year_page = site.get( + '/blog@index-pages/year/{:04d}'.format(year) + ) %} + {% if year_page %} + <li> + {% if year == current_year %} + <a href="{{ year_page | url }}" aria-current="page">{{ year }}</a> + {% else %} + <a href="{{ year_page | url }}" aria-label="Year {{ year }}">{{ year }}</a> + {% endif %} + </li> + {% endif %} + {% endfor %} + {% if window_end < last_year %} + {% if window_end < last_year - 1 %} + <li> + <span aria-hidden="true">…</span> + </li> + {% endif %} + <li> + <a href="{{ last_year_page | url }}" aria-label="Year {{ last_year }}">{{ last_year }}</a> + </li> + {% endif %} + <li> + {% if next_year_page %} + <a href="{{ next_year_page | url }}" + rel="next" + aria-label="Next year: {{ next_year_page.year }}">❯</a> + {% else %} + <span aria-disabled="true">❯</span> + {% endif %} + </li> + </ol> + </nav> +{% 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 %} + <nav aria-label="Monthly archive navigation"> + <ol> + <li> + {% if previous_month_page %} + <a href="{{ previous_month_page | url }}" + rel="prev" + aria-label="Previous month: {{ previous_month_page.date | dateformat('yyyy-MM') }}">❮</a> + {% else %} + <span aria-disabled="true">❮</span> + {% endif %} + </li> + {% if window_start > 0 %} + <li> + <a href="{{ first_month_page | url }}" + aria-label="{{ first_month_page.date | dateformat('yyyy-MM') }}"> + {{ first_month_page.date | dateformat("MMMM y") }} + </a> + </li> + {% if window_start > 1 %} + <li> + <span aria-hidden="true">…</span> + </li> + {% endif %} + {% endif %} + {% for position in range(window_start, window_end + 1) %} + {% set month_page = ns.month_pages[position] %} + <li> + {% if position == current_position %} + <a href="{{ month_page | url }}" aria-current="page">{{ month_page.date | dateformat("MMMM y") }}</a> + {% else %} + <a href="{{ month_page | url }}" + aria-label="{{ month_page.date | dateformat('yyyy-MM') }}"> + {{ month_page.date | dateformat("MMMM y") }} + </a> + {% endif %} + </li> + {% endfor %} + {% if window_end < total - 1 %} + {% if window_end < total - 2 %} + <li> + <span aria-hidden="true">…</span> + </li> + {% endif %} + <li> + <a href="{{ last_month_page | url }}" + aria-label="{{ last_month_page.date | dateformat('yyyy-MM') }}"> + {{ last_month_page.date | dateformat("MMMM y") }} + </a> + </li> + {% endif %} + <li> + {% if next_month_page %} + <a href="{{ next_month_page | url }}" + rel="next" + aria-label="Next month: {{ next_month_page.date | dateformat('yyyy-MM') }}">❯</a> + {% else %} + <span aria-disabled="true">❯</span> + {% endif %} + </li> + </ol> + </nav> +{% 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 %}<article>{{ this.body }}</article>{% 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() }} + <meta property="og:type" content="website"> +{% endblock %} +{% block styles %} + {{ super() }} + <link rel="stylesheet" + type="text/css" + href="{{ '/static/css/category.css'|asseturl }}"> +{% endblock %} +{% set title = "Tag " + this.tag %} +{% block content %} + {% set post_count = this.items.count() %} + <article> + <header> + <hgroup> + <h2>Tag: {{ this.tag }}</h2> + <p> + ({{ post_count }} + {% if post_count > 1 %} + posts) + {% else %} + post) + {% endif %} + </p> + </hgroup> + </header> + <table> + <thead class="visually-hidden"> + <tr> + <th scope="col">Date</th> + <th scope="col">Title</th> + </tr> + </thead> + <tbody> + {% for post in this.items %} + <tr> + <td> + {% if post.publication_date %} + <time datetime="{{ post.publication_date }}">{{ post.publication_date }}</time> + {% endif %} + </td> + <td> + <a href="{{ post|url }}">{{ post.title }}</a> + </td> + </tr> + {% else %} + <tr> + <td colspan="2"> + <em>No posts for this tag.</em> + </td> + </tr> + {% endfor %} + </tbody> + </table> + </article> +{% endblock %} |
