summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIwanIDev <iwan@iwani.dev>2026-03-21 15:43:12 +0000
committerIwanIDev <iwan@iwani.dev>2026-03-21 15:43:12 +0000
commit42ca90ee1f0aadaa09888f1c9627657abcd866b2 (patch)
treea9291ad2afae2f7367924fcb8b36a36a6d79c497
parentc2ad94b04f290b5197a1cc80c98015be9e7b25da (diff)
feat: implement PostHeader component and add blog post route
-rw-r--r--src/components/BlogPosts.astro4
-rw-r--r--src/components/PostHeader.astro29
-rw-r--r--src/layouts/main.astro15
-rw-r--r--src/pages/posts/[...slug].astro42
4 files changed, 87 insertions, 3 deletions
diff --git a/src/components/BlogPosts.astro b/src/components/BlogPosts.astro
index 54b7529..73f9b5c 100644
--- a/src/components/BlogPosts.astro
+++ b/src/components/BlogPosts.astro
@@ -1,6 +1,6 @@
---
import { getCollection } from 'astro:content';
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import { Card, CardContent, CardDescription, CardHeader } from "@/components/ui/card";
// Fetch all blog posts from the content collection
const posts = await getCollection('blog');
@@ -18,7 +18,7 @@ const sortedPosts = posts.sort((a, b) =>
<a href={`/posts/${post.id}`} class="block hover:opacity-80 transition-opacity">
<Card>
<CardHeader>
- <CardTitle>{post.data.title}</CardTitle>
+ <h2 class="font-heading text-sm font-medium" transition:name={`post-title-${post.id}`}>{post.data.title}</h2>
<CardDescription>{post.data.description}</CardDescription>
</CardHeader>
<CardContent>
diff --git a/src/components/PostHeader.astro b/src/components/PostHeader.astro
new file mode 100644
index 0000000..1845ec1
--- /dev/null
+++ b/src/components/PostHeader.astro
@@ -0,0 +1,29 @@
+---
+import { Card, CardContent, CardDescription, CardHeader } from "@/components/ui/card";
+import { Badge } from "@/components/ui/badge";
+import { Separator } from "@/components/ui/separator";
+
+interface Props {
+ title: string;
+ slug: string;
+ description: string;
+ author: string;
+ publishDate: Date;
+}
+
+const { title, slug, description, author, publishDate } = Astro.props as Props;
+---
+
+<Card className="w-full">
+ <CardHeader>
+ <div class="flex flex-wrap gap-2">
+ <Badge variant="secondary">{author}</Badge>
+ <Badge variant="outline">{publishDate.toLocaleDateString()}</Badge>
+ </div>
+ <h1 class="font-heading text-xl font-medium md:text-2xl" transition:name={`post-title-${slug}`}>{title}</h1>
+ <CardDescription>{description}</CardDescription>
+ </CardHeader>
+ <CardContent>
+ <Separator />
+ </CardContent>
+</Card>
diff --git a/src/layouts/main.astro b/src/layouts/main.astro
index 69b80d2..30ac84d 100644
--- a/src/layouts/main.astro
+++ b/src/layouts/main.astro
@@ -1,13 +1,26 @@
---
+import { ViewTransitions } from "astro:transitions";
import "@/styles/global.css"
+
+interface Props {
+ title?: string;
+ description?: string;
+}
+
+const {
+ title = "Astro App",
+ description = "Astro blog"
+} = Astro.props as Props;
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
+ <ViewTransitions />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
- <title>Astro App</title>
+ <title>{title}</title>
+ <meta name="description" content={description} />
</head>
<body>
<slot />
diff --git a/src/pages/posts/[...slug].astro b/src/pages/posts/[...slug].astro
new file mode 100644
index 0000000..b68f153
--- /dev/null
+++ b/src/pages/posts/[...slug].astro
@@ -0,0 +1,42 @@
+---
+import { getCollection, type CollectionEntry } from "astro:content";
+import Layout from "@/layouts/main.astro";
+import { buttonVariants } from "@/components/ui/button";
+import PostHeader from "@/components/PostHeader.astro";
+
+export async function getStaticPaths() {
+ const posts = await getCollection("blog");
+
+ return posts.map((post) => ({
+ params: { slug: post.id },
+ props: { post },
+ }));
+}
+
+interface Props {
+ post: CollectionEntry<"blog">;
+}
+
+const { post } = Astro.props as Props;
+const { Content } = await post.render();
+---
+
+<Layout title={`${post.data.title} | Iwan Ingman's Blog`} description={post.data.description}>
+ <main class="mx-auto flex w-full max-w-3xl flex-col gap-8 p-4 md:p-6">
+ <PostHeader
+ title={post.data.title}
+ slug={post.id}
+ description={post.data.description}
+ author={post.data.author}
+ publishDate={post.data.publishDate}
+ />
+
+ <article class="space-y-4 leading-relaxed">
+ <Content />
+ </article>
+
+ <div>
+ <a class={buttonVariants({ size: "xs", variant: "outline" })} href="/">Back to posts</a>
+ </div>
+ </main>
+</Layout>