Add blog utilities and tag pages
CI / ci (push) Successful in 29s
CI / ci (pull_request) Successful in 29s
Docker / docker (pull_request) Successful in 17s

Introduce formatBlogDate, slugifyTag and getTagHref in src/lib/blog.ts
and
a reading time helper getReadingTime in src/lib/readingTime.ts. Update
blog index and post pages to display ISO dates, reading times and link
tags to /blog/tag/<slug>. Add a tag listing page at
src/pages/blog/tag/[tag].astro with styles and static paths generation
This commit is contained in:
2026-03-07 17:46:15 +01:00
parent 45059fcd53
commit 7e3299e3bd
5 changed files with 554 additions and 23 deletions
+78 -12
View File
@@ -1,6 +1,8 @@
---
import BaseLayout from "../../layouts/BaseLayout.astro";
import { getCollection, type CollectionEntry } from "astro:content";
import { formatBlogDate, getTagHref } from "../../lib/blog";
import { getReadingTime } from "../../lib/readingTime";
type BlogPost = CollectionEntry<"blog">;
@@ -69,10 +71,7 @@ const {
};
const { Content } = await post.render();
function formatDate(date: Date) {
return date.toISOString().split("T")[0];
}
const readingTime = getReadingTime(post.body);
---
<BaseLayout
@@ -92,7 +91,16 @@ function formatDate(date: Date) {
)
}
<h1 class="title">{post.data.title}</h1>
<p class="date">{formatDate(post.data.pubDate)}</p>
<div class="meta-row">
<time
class="date"
datetime={formatBlogDate(post.data.pubDate)}
>
{formatBlogDate(post.data.pubDate)}
</time>
<span class="meta-sep" aria-hidden="true">·</span>
<span class="reading-time">{readingTime.text}</span>
</div>
{
post.data.tags.length > 0 && (
<div
@@ -100,7 +108,9 @@ function formatDate(date: Date) {
aria-label={`${post.data.title} tags`}
>
{post.data.tags.map((tag) => (
<span class="tag">{tag}</span>
<a class="tag" href={getTagHref(tag)}>
{tag}
</a>
))}
</div>
)
@@ -185,11 +195,26 @@ function formatDate(date: Date) {
href={`/blog/${relatedPost.slug}`}
class="related-card"
>
<span class="related-date">
{formatDate(
relatedPost.data.pubDate,
)}
</span>
<div class="related-meta">
<span class="related-date">
{formatBlogDate(
relatedPost.data.pubDate,
)}
</span>
<span
class="related-sep"
aria-hidden="true"
>
·
</span>
<span class="related-reading-time">
{
getReadingTime(
relatedPost.body,
).text
}
</span>
</div>
<h3>{relatedPost.data.title}</h3>
<p>{relatedPost.data.description}</p>
</a>
@@ -283,6 +308,22 @@ function formatDate(date: Date) {
font-size: 0.85rem;
}
.meta-row {
display: flex;
align-items: center;
gap: var(--space-xs);
color: var(--color-text-dim);
font-size: 0.85rem;
}
.meta-sep {
color: var(--color-surface);
}
.reading-time {
color: var(--color-accent);
}
.tag-list {
display: flex;
flex-wrap: wrap;
@@ -296,6 +337,12 @@ function formatDate(date: Date) {
border: 1px solid var(--color-surface);
padding: 1px 6px;
border-radius: 999px;
text-decoration: none;
}
.tag:hover {
color: var(--color-accent-bright);
border-color: color-mix(in srgb, var(--color-accent) 45%, transparent);
}
.content {
@@ -547,12 +594,26 @@ function formatDate(date: Date) {
}
.related-date {
display: inline-block;
color: var(--color-text-dim);
font-size: 0.8rem;
}
.related-meta {
display: flex;
align-items: center;
gap: var(--space-xs);
margin-bottom: var(--space-xs);
}
.related-sep {
color: var(--color-surface);
}
.related-reading-time {
color: var(--color-accent);
font-size: 0.78rem;
}
.related-card h3 {
font-size: 1rem;
color: var(--color-accent-bright);
@@ -625,6 +686,11 @@ function formatDate(date: Date) {
flex-direction: column;
align-items: flex-start;
}
.meta-row,
.related-meta {
flex-wrap: wrap;
}
}
@media (prefers-reduced-motion: reduce) {