Skip to content

Commit e11d173

Browse files
authored
fix: align reading and writing cards with responsive grid (#2)
1 parent 0bf7d3d commit e11d173

3 files changed

Lines changed: 75 additions & 49 deletions

File tree

src/pages/Reading.tsx

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { Book, FileText, ExternalLink, CheckCircle, Clock } from "lucide-react";
22
import { Badge } from "@/components/ui/badge";
3-
import { Button } from "@/components/ui/button";
4-
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
53
import { readingData, ReadingItem } from "../data/reading";
64

75
const Reading = () => {
@@ -11,8 +9,11 @@ const Reading = () => {
119
const StatusIcon = item.status === "finished" ? CheckCircle : Clock;
1210

1311
return (
14-
<Card key={`${item.title}-${item.author}`} className="group hover:shadow-lg transition-shadow duration-200">
15-
<CardHeader className="pb-3">
12+
<article
13+
key={`${item.title}-${item.author}`}
14+
className="group flex flex-col rounded-xl border border-border bg-card p-6 shadow-sm transition hover:-translate-y-0.5 hover:shadow-md focus-within:-translate-y-0.5 focus-within:shadow-md"
15+
>
16+
<header className="pb-3">
1617
<div className="flex items-start justify-between">
1718
<div className="flex items-center space-x-2">
1819
<TypeIcon className="h-5 w-5 text-muted-foreground" />
@@ -25,28 +26,32 @@ const Reading = () => {
2526
{item.status}
2627
</Badge>
2728
</div>
28-
<CardTitle className="text-lg leading-tight">{item.title}</CardTitle>
29+
<h3 className="mt-2 text-lg font-semibold leading-snug">{item.title}</h3>
2930
<p className="text-sm text-muted-foreground">by {item.author}</p>
30-
</CardHeader>
31+
</header>
32+
<div className="mt-3 grow" />
3133
{item.link && (
32-
<CardContent className="pt-0">
33-
<Button variant="ghost" size="sm" asChild className="w-full justify-start">
34-
<a href={item.link} target="_blank" rel="noopener noreferrer">
35-
<ExternalLink className="h-4 w-4 mr-2" />
36-
View {item.type === "book" ? "Book" : "Paper"}
37-
</a>
38-
</Button>
39-
</CardContent>
34+
<footer className="mt-4">
35+
<a
36+
href={item.link}
37+
target="_blank"
38+
rel="noopener noreferrer"
39+
className="inline-flex items-center text-sm underline underline-offset-2 hover:text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50"
40+
>
41+
<ExternalLink className="h-4 w-4 mr-2" />
42+
View {item.type === "book" ? "Book" : "Paper"}
43+
</a>
44+
</footer>
4045
)}
41-
</Card>
46+
</article>
4247
);
4348
};
4449

4550
const books = readingData.filter(item => item.type === "book");
4651
const papers = readingData.filter(item => item.type === "paper");
4752

4853
return (
49-
<div className="max-w-6xl mx-auto space-y-8">
54+
<div className="mx-auto max-w-[1200px] px-4 md:px-6 space-y-8">
5055
<div className="text-center space-y-4">
5156
<h1 className="text-3xl md:text-4xl font-bold">Shelf</h1>
5257
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
@@ -63,7 +68,7 @@ const Reading = () => {
6368
<h2 className="text-2xl font-semibold">Books</h2>
6469
<Badge variant="secondary">{books.length}</Badge>
6570
</div>
66-
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
71+
<div className="grid gap-4 md:gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 [grid-template-columns:repeat(auto-fit,minmax(320px,1fr))]">
6772
{books.map(renderReadingCard)}
6873
</div>
6974
</section>
@@ -77,7 +82,7 @@ const Reading = () => {
7782
<h2 className="text-2xl font-semibold">Research Papers</h2>
7883
<Badge variant="secondary">{papers.length}</Badge>
7984
</div>
80-
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
85+
<div className="grid gap-4 md:gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 [grid-template-columns:repeat(auto-fit,minmax(320px,1fr))]">
8186
{papers.map(renderReadingCard)}
8287
</div>
8388
</section>

src/pages/Writing.tsx

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { useState, useEffect } from "react";
2-
import { ExternalLink, Calendar, Clock } from "lucide-react";
2+
import { ExternalLink, Calendar } from "lucide-react";
33
import { Button } from "@/components/ui/button";
4-
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
5-
import { Badge } from "@/components/ui/badge";
64
import { Skeleton } from "@/components/ui/skeleton";
75

86
interface BlogPost {
@@ -13,14 +11,22 @@ interface BlogPost {
1311
thumbnail?: string;
1412
}
1513

14+
interface RSSItem {
15+
title: string;
16+
link: string;
17+
pubDate: string;
18+
description: string;
19+
thumbnail?: string;
20+
}
21+
1622
const Writing = () => {
1723
const [posts, setPosts] = useState<BlogPost[]>([]);
1824
const [loading, setLoading] = useState(true);
1925
const [error, setError] = useState<string | null>(null);
2026

2127
const formatDescription = (htmlDescription: string): string => {
2228
// Remove HTML tags but preserve some structure
23-
let text = htmlDescription
29+
const text = htmlDescription
2430
.replace(/<\/p>/g, '\n\n') // Convert closing p tags to double line breaks
2531
.replace(/<\/h[1-6]>/g, '\n\n') // Convert closing header tags to double line breaks
2632
.replace(/<br\s*\/?>/g, '\n') // Convert br tags to line breaks
@@ -62,7 +68,7 @@ const Writing = () => {
6268
const data = await response.json();
6369

6470
if (data.status === "ok") {
65-
const formattedPosts = data.items.slice(0, 6).map((item: any) => ({
71+
const formattedPosts = data.items.slice(0, 6).map((item: RSSItem) => ({
6672
title: item.title,
6773
link: item.link,
6874
pubDate: item.pubDate,
@@ -96,30 +102,36 @@ const Writing = () => {
96102

97103
if (loading) {
98104
return (
99-
<div className="max-w-6xl mx-auto space-y-8">
105+
<div className="mx-auto max-w-[1200px] px-4 md:px-6 space-y-8">
100106
<div className="text-center space-y-4">
101107
<h1 className="text-3xl md:text-4xl font-bold">Writing</h1>
102108
<p className="text-lg text-muted-foreground">Loading latest posts...</p>
103109
</div>
104-
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
110+
<div className="grid gap-4 md:gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 [grid-template-columns:repeat(auto-fit,minmax(320px,1fr))]">
105111
{[...Array(6)].map((_, i) => (
106-
<Card key={i}>
107-
<CardHeader>
112+
<article
113+
key={i}
114+
className="flex flex-col rounded-xl border border-border bg-card p-6 shadow-sm"
115+
>
116+
<header className="space-y-2">
108117
<Skeleton className="h-4 w-3/4" />
109118
<Skeleton className="h-3 w-1/2" />
110-
</CardHeader>
111-
<CardContent>
119+
</header>
120+
<div className="mt-3 grow">
112121
<Skeleton className="h-20 w-full" />
113-
</CardContent>
114-
</Card>
122+
</div>
123+
<footer className="mt-4">
124+
<Skeleton className="h-4 w-24" />
125+
</footer>
126+
</article>
115127
))}
116128
</div>
117129
</div>
118130
);
119131
}
120132

121133
return (
122-
<div className="max-w-6xl mx-auto space-y-8">
134+
<div className="mx-auto max-w-[1200px] px-4 md:px-6 space-y-8">
123135
<div className="text-center space-y-4">
124136
<h1 className="text-3xl md:text-4xl font-bold">Writing</h1>
125137
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
@@ -142,30 +154,38 @@ const Writing = () => {
142154
</Button>
143155
</div>
144156

145-
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
157+
<div className="grid gap-4 md:gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 [grid-template-columns:repeat(auto-fit,minmax(320px,1fr))]">
146158
{posts.map((post, index) => (
147-
<Card key={index} className="group hover:shadow-lg transition-shadow duration-200">
148-
<CardHeader className="pb-3">
149-
<div className="flex items-center space-x-2 text-sm text-muted-foreground mb-2">
159+
<article
160+
key={index}
161+
className="group flex flex-col rounded-xl border border-border bg-card p-6 shadow-sm transition hover:-translate-y-0.5 hover:shadow-md focus-within:-translate-y-0.5 focus-within:shadow-md"
162+
>
163+
<header>
164+
<div className="mb-2 flex items-center space-x-2 text-sm text-muted-foreground">
150165
<Calendar className="h-4 w-4" />
151166
<span>{formatDate(post.pubDate)}</span>
152167
</div>
153-
<CardTitle className="text-lg leading-tight group-hover:text-primary transition-colors">
168+
<h3 className="text-lg font-semibold leading-snug group-hover:text-primary transition-colors">
154169
{post.title}
155-
</CardTitle>
156-
</CardHeader>
157-
<CardContent className="space-y-4">
170+
</h3>
171+
</header>
172+
<div className="mt-3 grow">
158173
<p className="text-sm text-muted-foreground whitespace-pre-line leading-relaxed">
159174
{post.description}
160175
</p>
161-
<Button variant="outline" size="sm" asChild className="w-full">
162-
<a href={post.link} target="_blank" rel="noopener noreferrer">
163-
<ExternalLink className="h-4 w-4 mr-2" />
164-
Read on Medium
165-
</a>
166-
</Button>
167-
</CardContent>
168-
</Card>
176+
</div>
177+
<footer className="mt-4">
178+
<a
179+
href={post.link}
180+
target="_blank"
181+
rel="noopener noreferrer"
182+
className="inline-flex items-center text-sm underline underline-offset-2 hover:text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50"
183+
>
184+
<ExternalLink className="h-4 w-4 mr-2" />
185+
Read on Medium
186+
</a>
187+
</footer>
188+
</article>
169189
))}
170190
</div>
171191

tailwind.config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Config } from "tailwindcss";
2+
import animate from "tailwindcss-animate";
23

34
export default {
45
darkMode: ["class"],
@@ -92,5 +93,5 @@ export default {
9293
}
9394
}
9495
},
95-
plugins: [require("tailwindcss-animate")],
96+
plugins: [animate],
9697
} satisfies Config;

0 commit comments

Comments
 (0)