feat: add blog with archives, pagination, search, sharing and RSS

- Content collection (one folder per post) with zod schema
- /blog, category and tag archives with /sayfa/<n>/ pagination
- Post page: breadcrumbs, date badges, share bar, TOC, DIY notice,
  code copy, license line, prev/next, BlogPosting + BreadcrumbList JSON-LD
- Sidebar: Pagefind search, categories, featured fallback, tag cloud
- Sätteri HAST plugins for figure/figcaption and external links
- Dual-theme Shiki, themed prose, RSS feed, latest posts on home
- Three sample posts marked as examples

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-16 01:06:07 +03:00
parent f2d943c31b
commit 809f89af08
27 changed files with 1599 additions and 27 deletions
+71
View File
@@ -0,0 +1,71 @@
---
// Shared layout for /blog/, category and tag archives (all paginated).
import BaseLayout from '../../layouts/BaseLayout.astro';
import { blog, fmt } from '../../lib/data';
import type { Post } from '../../lib/posts';
import Container from '../Container.astro';
import GridLines from '../GridLines.astro';
import SectionHeading from '../SectionHeading.astro';
import Pager from './Pager.astro';
import PostCard from './PostCard.astro';
import Sidebar from './Sidebar.astro';
export interface Props {
eyebrow: string;
title: string;
description: string;
/** Posts on this page. */
posts: Post[];
/** All published posts, for the sidebar. */
allPosts: Post[];
current: number;
total: number;
base: string;
activeCategory?: string;
activeTag?: string;
jsonLd?: Record<string, unknown>[];
}
const {
eyebrow,
title,
description,
posts,
allPosts,
current,
total,
base,
activeCategory,
activeTag,
jsonLd,
} = Astro.props;
const seoTitle = current > 1 ? fmt(blog.list.pageTitle, { title, n: current }) : title;
---
<BaseLayout title={seoTitle} description={description} jsonLd={jsonLd}>
<section class="surface-alt relative overflow-hidden">
<GridLines />
<Container class="relative py-14 md:py-20">
<SectionHeading level={1} eyebrow={eyebrow} title={title} />
<p class="mt-4 max-w-2xl text-lg">{description}</p>
</Container>
</section>
<Container class="grid gap-12 py-14 lg:grid-cols-[minmax(0,1fr)_20rem]">
<div>
{posts.length ? (
<ul class="flex flex-col gap-8">
{posts.map((post, i) => (
<li>
<PostCard post={post} priority={i === 0} />
</li>
))}
</ul>
) : (
<p class="border-fg/30 border-2 border-dashed p-8 text-center text-lg">{blog.list.empty}</p>
)}
<Pager current={current} total={total} base={base} />
</div>
<Sidebar posts={allPosts} activeCategory={activeCategory} activeTag={activeTag} />
</Container>
</BaseLayout>