11import type { Metadata } from 'next'
22import Link from 'next/link'
3- import { PostCard } from 'nextra-theme-blog'
4- import { getPosts , getTags } from './get-posts'
5- import type { TagCount , PostCardData } from '../../types/blog'
3+ import { getPosts } from './get-posts'
64
75const BLOG_TITLE = 'Blog'
86
@@ -18,51 +16,65 @@ export const metadata: Metadata = {
1816}
1917
2018export default async function BlogIndexPage ( ) {
21- const tags = await getTags ( )
2219 const posts = await getPosts ( )
23- const allTags : TagCount = { }
24-
25- for ( const tag of tags ) {
26- allTags [ tag ] = ( allTags [ tag ] ?? 0 ) + 1
27- }
2820
2921 return (
30- < div data-pagefind-ignore = "all" >
31- < h1 > { BLOG_TITLE } </ h1 >
32- { Object . keys ( allTags ) . length > 0 && (
33- < div
34- className = "not-prose"
35- style = { { display : 'flex' , flexWrap : 'wrap' , gap : '.5rem' , marginBottom : '2rem' } }
36- >
37- { Object . entries ( allTags ) . map ( ( [ tag , count ] ) => (
38- < Link
39- key = { tag }
40- href = { `/blog/tags/${ tag } ` }
41- className = "nextra-tag"
42- >
43- { tag } ({ count } )
44- </ Link >
45- ) ) }
46- </ div >
47- ) }
48- { posts . map ( post => {
49- // Transform blog post data to match PostCard component expectations
50- // PostCard from nextra-theme-blog expects frontMatter to be BlogMetadata
51- // (only title and description), not our extended frontMatter with date, tags, etc.
52- const postCardData : PostCardData = {
53- ...post ,
54- frontMatter : {
55- title : post . frontMatter . title || post . name ,
56- description : post . frontMatter . description
57- }
58- }
59- return (
60- < PostCard
61- key = { post . route }
62- post = { postCardData }
63- />
64- )
65- } ) }
66- </ div >
22+ < main className = "mx-auto max-w-3xl px-6 py-10 md:px-10" >
23+ < header className = "mb-10" >
24+ < h1 className = "text-4xl font-bold tracking-tight text-slate-900 dark:text-slate-100" >
25+ { BLOG_TITLE }
26+ </ h1 >
27+ < p className = "mt-3 text-lg leading-8 text-slate-600 dark:text-slate-300" >
28+ Articles about Happy, Claude Code workflows, distribution, and practical engineering.
29+ </ p >
30+ </ header >
31+
32+ < div className = "space-y-6" data-pagefind-ignore = "all" >
33+ { posts
34+ . filter ( post => post . route !== '/blog' )
35+ . map ( post => {
36+ const title = post . frontMatter . title || post . name
37+ const description = post . frontMatter . description || ''
38+ const date = post . frontMatter . date
39+ ? new Intl . DateTimeFormat ( 'en' , {
40+ year : 'numeric' ,
41+ month : 'short' ,
42+ day : 'numeric' ,
43+ } ) . format ( new Date ( post . frontMatter . date ) )
44+ : null
45+
46+ return (
47+ < article
48+ key = { post . route }
49+ className = "rounded-2xl border border-slate-200 p-6 transition-colors hover:border-slate-300 dark:border-slate-800 dark:hover:border-slate-700"
50+ >
51+ { date && (
52+ < p className = "mb-3 text-sm font-medium uppercase tracking-[0.12em] text-slate-500 dark:text-slate-400" >
53+ { date }
54+ </ p >
55+ ) }
56+ < h2 className = "text-2xl font-semibold tracking-tight text-slate-900 dark:text-slate-100" >
57+ < Link href = { `${ post . route } /` } className = "hover:text-slate-700 dark:hover:text-slate-300" >
58+ { title }
59+ </ Link >
60+ </ h2 >
61+ { description && (
62+ < p className = "mt-3 text-base leading-7 text-slate-600 dark:text-slate-300" >
63+ { description }
64+ </ p >
65+ ) }
66+ < div className = "mt-5" >
67+ < Link
68+ href = { `${ post . route } /` }
69+ className = "text-sm font-semibold text-slate-900 hover:text-slate-700 dark:text-slate-100 dark:hover:text-slate-300"
70+ >
71+ Read article
72+ </ Link >
73+ </ div >
74+ </ article >
75+ )
76+ } ) }
77+ </ div >
78+ </ main >
6779 )
6880}
0 commit comments