@@ -4,6 +4,8 @@ import { motion } from 'framer-motion';
44import Image from 'next/image' ;
55import Link from 'next/link' ;
66
7+ import authorsData from '@/assets/data/authors.json' ;
8+
79type FeaturedProps = {
810 slug : string ;
911 title : string ;
@@ -13,6 +15,7 @@ type FeaturedProps = {
1315 heroImage : string ;
1416 imageAuthor ?: string ;
1517 category ?: string ;
18+ authors ?: string [ ] ;
1619} ;
1720
1821const cardV = {
@@ -31,76 +34,128 @@ export default function BlogFeatured({
3134 heroImage,
3235 imageAuthor,
3336 category,
37+ authors,
3438} : FeaturedProps ) {
39+ const resolvedAuthors = ( authors ?? [ ] ) . flatMap ( ( id ) => {
40+ const data = authorsData [ id as keyof typeof authorsData ] ;
41+ return data ? [ { id, ...data } ] : [ ] ;
42+ } ) ;
43+
3544 return (
3645 < motion . article
3746 initial = "rest"
3847 whileHover = "hover"
3948 animate = "rest"
4049 variants = { cardV }
4150 transition = { { type : `spring` , stiffness : 220 , damping : 24 } }
42- className = "group mx-auto w-full max-w-5xl overflow-hidden rounded-2xl border border-slate-200 bg-white"
51+ className = "group relative mx-auto w-full max-w-5xl overflow-hidden rounded-2xl border border-slate-200 bg-white"
4352 itemScope
4453 itemType = "https://schema.org/BlogPosting"
4554 >
46- < Link href = { `/blog/${ slug } ` } className = "relative block" >
47- < motion . div
48- variants = { mediaV }
49- transition = { { duration : 0.25 } }
50- className = "relative aspect-[16/9] max-h-[420px] w-full overflow-hidden bg-slate-100 md:aspect-[5/2] md:max-h-[440px] lg:aspect-[21/9] lg:max-h-[460px]"
51- >
52- < Image
53- src = { heroImage }
54- alt = { imageAuthor || title }
55- fill
56- sizes = "(min-width:1280px) 1100px, 100vw"
57- className = "object-cover"
58- priority
59- />
60- </ motion . div >
61- < div className = "p-5 sm:p-6" >
62- < div className = "mb-2 flex flex-wrap items-center gap-2 text-xs text-slate-600" >
63- < time dateTime = { date } > { dayjs ( date ) . format ( `MMMM D, YYYY` ) } </ time >
64- < span className = "text-slate-300 select-none" > •</ span >
65- < span > { timeToRead } min read</ span >
66- { category && (
67- < >
68- < span className = "text-slate-300 select-none" > •</ span >
69- < div
70- aria-label = { `View posts in ${ category } category` }
71- className = "rounded-full border border-slate-300 px-2 py-0.5 text-[11px] font-medium text-slate-700 hover:border-slate-400 hover:text-slate-900"
55+ < motion . div
56+ variants = { mediaV }
57+ transition = { { duration : 0.25 } }
58+ className = "relative aspect-[16/9] max-h-[420px] w-full overflow-hidden bg-slate-100 md:aspect-[5/2] md:max-h-[440px] lg:aspect-[21/9] lg:max-h-[460px]"
59+ >
60+ < Image
61+ src = { heroImage }
62+ alt = { imageAuthor || title }
63+ fill
64+ sizes = "(min-width:1280px) 1100px, 100vw"
65+ className = "object-cover"
66+ priority
67+ />
68+ </ motion . div >
69+
70+ < div className = "p-5 sm:p-6" >
71+ < div className = "mb-2 flex flex-wrap items-center gap-2 text-xs text-slate-600" >
72+ { resolvedAuthors . length > 0 && (
73+ < >
74+ < div className = "relative z-10 flex items-center gap-1.5" >
75+ < Link
76+ href = { `/authors/${ resolvedAuthors [ 0 ] . id } ` }
77+ className = "flex -space-x-1.5 transition-opacity hover:opacity-80"
78+ tabIndex = { - 1 }
79+ aria-hidden
7280 >
73- { category }
74- </ div >
75- </ >
76- ) }
77- </ div >
81+ { resolvedAuthors . slice ( 0 , 3 ) . map ( ( author ) => (
82+ < div
83+ key = { author . name }
84+ className = "relative h-6 w-6 overflow-hidden rounded-full ring-2 ring-white"
85+ >
86+ < Image
87+ src = { author . avatar }
88+ alt = { author . name }
89+ fill
90+ sizes = "24px"
91+ className = "object-cover"
92+ />
93+ </ div >
94+ ) ) }
95+ </ Link >
96+ < span >
97+ { resolvedAuthors . map ( ( author , i ) => (
98+ < span key = { author . id } >
99+ { i > 0 && < span className = "mr-0.5" > ,</ span > }
100+ < Link
101+ href = { `/authors/${ author . id } ` }
102+ className = "hover:underline"
103+ >
104+ { author . name }
105+ </ Link >
106+ </ span >
107+ ) ) }
108+ </ span >
109+ </ div >
110+ < span className = "text-slate-300 select-none" > •</ span >
111+ </ >
112+ ) }
113+ < time dateTime = { date } > { dayjs ( date ) . format ( `MMMM D, YYYY` ) } </ time >
114+ < span className = "text-slate-300 select-none" > •</ span >
115+ < span > { timeToRead } min read</ span >
116+ { category && (
117+ < >
118+ < span className = "text-slate-300 select-none" > •</ span >
119+ < div
120+ aria-label = { `View posts in ${ category } category` }
121+ className = "rounded-full border border-slate-300 px-2 py-0.5 text-[11px] font-medium text-slate-700 hover:border-slate-400 hover:text-slate-900"
122+ >
123+ { category }
124+ </ div >
125+ </ >
126+ ) }
127+ </ div >
78128
79- < motion . h2
80- variants = { titleV }
81- transition = { { duration : 0.2 } }
82- className = "group-hover:text-primary text-2xl leading-tight font-bold text-balance text-slate-900 group-hover:underline sm:text-3xl md:text-[1.9rem]"
83- >
84- { title }
85- </ motion . h2 >
129+ < motion . h2
130+ variants = { titleV }
131+ transition = { { duration : 0.2 } }
132+ className = "group-hover:text-primary text-2xl leading-tight font-bold text-balance text-slate-900 group-hover:underline sm:text-3xl md:text-[1.9rem]"
133+ >
134+ { title }
135+ </ motion . h2 >
86136
87- { subtitle && (
88- < p className = "mt-2 max-w-3xl text-base text-slate-700 md:text-lg" >
89- { subtitle }
90- </ p >
91- ) }
137+ { subtitle && (
138+ < p className = "mt-2 max-w-3xl text-base text-slate-700 md:text-lg" >
139+ { subtitle }
140+ </ p >
141+ ) }
92142
93- < div className = "mt-4" >
94- < div className = "group-hover:text-primary text-sm font-medium underline-offset-4 hover:underline" >
95- < span > Read the full story</ span >
96- < Icon
97- icon = "solar:arrow-right-broken"
98- className = "ml-1 inline-block h-4 w-4"
99- />
100- </ div >
143+ < div className = "mt-4" >
144+ < div className = "group-hover:text-primary text-sm font-medium underline-offset-4 hover:underline" >
145+ < span > Read the full story</ span >
146+ < Icon
147+ icon = "solar:arrow-right-broken"
148+ className = "ml-1 inline-block h-4 w-4"
149+ />
101150 </ div >
102151 </ div >
103- </ Link >
152+ </ div >
153+
154+ < Link
155+ href = { `/blog/${ slug } ` }
156+ className = "absolute inset-0"
157+ aria-label = { title }
158+ />
104159 </ motion . article >
105160 ) ;
106161}
0 commit comments