Files
andwati.com/templates/page.html
T
andwati a49aa02886 Auto-generate per-post OG images
Renders a 1200x630 social card per post (satori + resvg-js, using the
site's own self-hosted fonts) instead of reusing one generic og-image.png
for every post. Wired into npm run build via a new generate:og step;
templates/page.html falls back to the per-slug image unless extra.image
is set, and non-post pages keep the generic fallback.
2026-08-19 10:56:40 +03:00

211 lines
8.5 KiB
HTML

{% extends "base.html" %}
{% block title %}{{ page.title }} | {{ config.title }}{% endblock title %}
{% block description %}{{ page.description | default(value=config.description) }}{% endblock description %}
{% block canonical %}{{ page.permalink }}{% endblock canonical %}
{# Open Graph overrides for article pages #}
{% block og_title %}{{ page.title }}{% endblock og_title %}
{% block og_description %}{{ page.description | default(value=config.description) }}{% endblock og_description %}
{% block og_url %}{{ page.permalink }}{% endblock og_url %}
{% block og_type %}{% if "posts/_index.md" in page.ancestors %}article{% else %}website{% endif %}{% endblock og_type %}
{% block og_image %}{{ config.base_url }}{% if page.extra.image %}{{ page.extra.image }}{% elif "posts/_index.md" in page.ancestors %}/images/og/{{ page.slug }}.png{% else %}/images/og-image.png{% endif %}{% endblock og_image %}
{% block og_extra %}
{% if "posts/_index.md" in page.ancestors %}
{% if page.date %}<meta property="article:published_time" content="{{ page.date | date(format="%Y-%m-%dT%H:%M:%S%:z") }}">{% endif %}
{% if page.updated %}<meta property="article:modified_time" content="{{ page.updated | date(format="%Y-%m-%dT%H:%M:%S%:z") }}">{% endif %}
<meta property="article:author" content="{{ config.extra.author }}">
{% if page.taxonomies.tags %}
{% for tag in page.taxonomies.tags %}
<meta property="article:tag" content="{{ tag }}">
{% endfor %}
{% endif %}
{% endif %}
{% endblock og_extra %}
{# Twitter Card overrides #}
{% block twitter_title %}{{ page.title }}{% endblock twitter_title %}
{% block twitter_description %}{{ page.description | default(value=config.description) }}{% endblock twitter_description %}
{% block twitter_image %}{{ config.base_url }}{% if page.extra.image %}{{ page.extra.image }}{% elif "posts/_index.md" in page.ancestors %}/images/og/{{ page.slug }}.png{% else %}/images/og-image.png{% endif %}{% endblock twitter_image %}
{% block head %}
{% if "posts/_index.md" in page.ancestors %}
<!-- SEO: BlogPosting JSON-LD -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"@id": "{{ page.permalink | safe }}#article",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "{{ page.permalink | safe }}"
},
"headline": {{ page.title | json_encode | safe }},
"description": {% if page.description %}{{ page.description | json_encode | safe }}{% else %}{{ config.description | json_encode | safe }}{% endif %},
"image": "{{ config.base_url | safe }}{% if page.extra.image %}{{ page.extra.image | safe }}{% elif "posts/_index.md" in page.ancestors %}/images/og/{{ page.slug | safe }}.png{% else %}/images/og-image.png{% endif %}",
"url": "{{ page.permalink | safe }}",
"inLanguage": "en-US",
{% if page.date %}"datePublished": "{{ page.date | date(format="%Y-%m-%dT%H:%M:%S%:z") }}",{% endif %}
{% if page.updated %}"dateModified": "{{ page.updated | date(format="%Y-%m-%dT%H:%M:%S%:z") }}",{% else %}{% if page.date %}"dateModified": "{{ page.date | date(format="%Y-%m-%dT%H:%M:%S%:z") }}",{% endif %}{% endif %}
"author": {
"@type": "Person",
"@id": "{{ config.base_url | safe }}/#author",
"name": {{ config.extra.author | json_encode | safe }},
"url": "{{ config.base_url | safe }}/about/"
},
"publisher": {
"@type": "Person",
"@id": "{{ config.base_url | safe }}/#author",
"name": {{ config.extra.author | json_encode | safe }},
"url": "{{ config.base_url | safe }}/about/"
},
"isPartOf": {
"@type": "Blog",
"@id": "{{ config.base_url | safe }}/#blog",
"name": {{ config.title | json_encode | safe }},
"url": "{{ config.base_url | safe }}/"
}
{% if page.taxonomies.tags %}
,"keywords": [{% for tag in page.taxonomies.tags %}{{ tag | json_encode | safe }}{% if not loop.last %},{% endif %}{% endfor %}]
{% endif %}
}
</script>
<!-- SEO: BreadcrumbList JSON-LD -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "{{ config.base_url | safe }}/" },
{ "@type": "ListItem", "position": 2, "name": "Posts", "item": "{{ config.base_url | safe }}/posts/" },
{ "@type": "ListItem", "position": 3, "name": {{ page.title | json_encode | safe }}, "item": "{{ page.permalink | safe }}" }
]
}
</script>
{% else %}
<!-- SEO: WebPage JSON-LD -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebPage",
"@id": "{{ page.permalink | safe }}",
"name": {{ page.title | json_encode | safe }},
"description": {% if page.description %}{{ page.description | json_encode | safe }}{% else %}{{ config.description | json_encode | safe }}{% endif %},
"url": "{{ page.permalink | safe }}",
"inLanguage": "en-US",
"isPartOf": {
"@type": "WebSite",
"@id": "{{ config.base_url | safe }}/#website"
}
}
</script>
<!-- SEO: BreadcrumbList JSON-LD -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "{{ config.base_url | safe }}/" },
{ "@type": "ListItem", "position": 2, "name": {{ page.title | json_encode | safe }}, "item": "{{ page.permalink | safe }}" }
]
}
</script>
{% endif %}
{% if page.extra.math %}
<link rel="stylesheet" href="{{ get_url(path="css/katex/katex.min.css") }}">
<script defer src="{{ get_url(path="js/katex/katex.min.js") }}"></script>
<script defer src="{{ get_url(path="js/katex/auto-render.min.js") }}"
onload="renderMathInElement(document.body);"></script>
{% endif %}
{% if page.extra.mermaid %}
<script type="module">
import mermaid from '{{ get_url(path="js/mermaid.min.js") }}';
mermaid.initialize({ startOnLoad: true, theme: 'dark' });
</script>
{% endif %}
<!-- Reading Progress Bar -->
<script defer src="{{ get_url(path="js/progress.js") }}"></script>
<!-- Footnote backlinks -->
<script defer src="{{ get_url(path="js/footnotes.js") }}"></script>
<!-- Heading anchor links -->
<script defer src="{{ get_url(path="js/heading-anchors.js") }}"></script>
<!-- Sticky TOC active-section highlighting -->
{% if page.toc %}
<script defer src="{{ get_url(path="js/toc.js") }}"></script>
{% endif %}
{% endblock head %}
{% block content %}
<div class="reading-progress-container" aria-hidden="true">
<div class="reading-progress-bar" id="reading-progress-bar"></div>
</div>
<div class="post-layout">
<article class="post-detail">
<header class="post-header">
<h1 class="post-title">{{ page.title }}</h1>
<div class="post-meta">
{% if page.date %}
<span class="post-meta-item">
<time datetime="{{ page.date }}">{{ page.date | date(format="%B %e, %Y") }}</time>
</span>
{% endif %}
{% if page.updated and page.updated != page.date %}
<span class="post-meta-sep" aria-hidden="true">·</span>
<span class="post-meta-item post-meta-updated">
<span class="post-meta-label">Updated</span>
<time datetime="{{ page.updated }}">{{ page.updated | date(format="%B %e, %Y") }}</time>
</span>
{% endif %}
{% if page.reading_time %}
<span class="post-meta-sep" aria-hidden="true">·</span>
<span class="post-meta-item">
{{ page.reading_time }} min read
</span>
{% endif %}
</div>
</header>
{% include "partials/series-nav.html" %}
{% include "partials/toc.html" %}
<div class="post-body">
{{ page.content | safe }}
</div>
{% include "partials/post-nav.html" %}
{% include "partials/related-posts.html" %}
<footer class="post-footer">
<div class="post-tags-share">
<div class="post-tags">
{% if page.taxonomies.tags %}
{% for tag in page.taxonomies.tags %}
<a href="{{ get_taxonomy_url(kind="tags", term=tag) }}" class="tag">#{{ tag }}</a>
{% endfor %}
{% endif %}
</div>
{% include "partials/social-share.html" %}
</div>
<!-- Utterances comments (posts only) -->
{% include "partials/comments.html" %}
</footer>
</article>
{% include "partials/toc-sidebar.html" %}
</div>
{% endblock content %}