Blog Card - Copy this React, Tailwind Component to your project
Import Image from 'next/image'; import Link from 'next/link'; import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { CalendarDays, Clock } from 'lucide react'; import { calculateReadingTime, formatDate } from '@/lib/utils'; export interface Post { id?: number; image: string; title: string; slug: string; content: string; summary: string; date: string; excerpt?: string; tags?: string[]; readingTime?: number; } export default function BlogCard({ post, className, }: { post: Post; className?: string; }) { const readingTime = calculateReadingTime(post?.content || ''); return ( <Card className={`group relative flex h full flex col overflow hidden transition all duration 300 hover:shadow lg dark:hover:shadow gray 800/50 ${className}`} > {/* Image */} <div className="relative aspect video w full shrink 0 overflow hidden"> <Image src={post.image} alt={post.title} fill sizes="(max width: 768px) 100vw, (max width: 1200px) 50vw, 33vw" className="object cover transition transform duration 500 group hover:scale 105" priority={false} /> <div className="absolute inset 0 bg gradient to t from black/70 to transparent opacity 0 transition opacity duration 300 group hover:opacity 100" /> </div> {/* Text Content */} <div className="flex flex 1 flex col"> <CardHeader className="space y 2.5"> {post.tags?.length ? ( <div className="flex flex wrap gap 2"> {post.tags.slice(0, 2).map((tag) => ( <Badge key={tag} variant="secondary" className="text xs"> {tag} </Badge> ))} {post.tags.length > 2 && ( <Badge variant="outline" className="text xs"> +{post.tags.length 2} </Badge> )} </div> ) : null} <CardTitle className="line clamp 2 text lg font semibold leading tight"> <Link href={`/blog/${post.slug}`} className="after:absolute after:inset 0"> {post.title} </Link> </CardTitle> </CardHeader> <CardContent className="pb 3 flex 1"> <CardDescription className="line clamp 3 text sm"> {post.summary} </CardDescription> </CardContent> <CardFooter className="flex flex wrap items center justify between gap 2 text sm text muted foreground mt auto"> <div className="flex items center gap 1.5"> <CalendarDays className="h 4 w 4" /> <time dateTime={post.date}>{formatDate(post.date)}</time> </div> {readingTime > 0 && ( <div className="flex items center gap 1.5"> <Clock className="h 4 w 4" /> <span>{readingTime} min read</span> </div> )} </CardFooter> </div> {/* Read More Button */} <div className="absolute bottom 4 right 4 opacity 0 transition opacity duration 300 group hover:opacity 100"> <Button variant="outline" size="sm"> <Link href={`/blog/${post.slug}`}>Read more</Link> </Button> </div> </Card> ); }
