@@ -19,14 +19,40 @@ const formattedDate = post.data.pubDate.toLocaleDateString(
1919);
2020
2121const categoryLabel = t (` blog.categories.${post .data .category } ` );
22+
23+ // Calculate reading time (roughly 200 words per minute for CJK, 250 for English)
24+ const content = post .body || ' ' ;
25+ const wordCount = content .length ;
26+ const wordsPerMinute = lang === ' en' ? 250 : 200 ;
27+ const readingTime = Math .max (1 , Math .ceil (wordCount / wordsPerMinute ));
28+ const readingTimeLabel = lang === ' zh' ? ` ${readingTime } 分钟 ` : lang === ' ja' ? ` ${readingTime } 分 ` : ` ${readingTime } min ` ;
29+
30+ // Check for hero image
31+ const heroImage = post .data .heroImage ;
2232---
2333
24- <article class =" post-card" >
34+ <article class ={ ` post-card ${ heroImage ? ' has-hero ' : ' ' } ` } >
2535 <a href ={ ` /${lang }/blog/${slug }/ ` } class =" post-card-link" >
36+ { heroImage && (
37+ <div class = " post-card-image" >
38+ <img src = { heroImage } alt = { post .data .title } loading = " lazy" />
39+ <div class = " post-card-image-overlay" ></div >
40+ </div >
41+ )}
42+
2643 <div class =" post-card-content" >
2744 <div class =" post-card-meta" >
2845 <span class =" post-card-category" >{ categoryLabel } </span >
46+ <span class =" post-card-divider" >·</span >
2947 <span class =" post-card-date" >{ formattedDate } </span >
48+ <span class =" post-card-divider" >·</span >
49+ <span class =" post-card-reading-time" >
50+ <svg width =" 14" height =" 14" viewBox =" 0 0 24 24" fill =" none" stroke =" currentColor" stroke-width =" 2" >
51+ <circle cx =" 12" cy =" 12" r =" 10" ></circle >
52+ <polyline points =" 12 6 12 12 16 14" ></polyline >
53+ </svg >
54+ { readingTimeLabel }
55+ </span >
3056 </div >
3157
3258 <h2 class =" post-card-title" >{ post .data .title } </h2 >
@@ -41,7 +67,12 @@ const categoryLabel = t(`blog.categories.${post.data.category}`);
4167 </div >
4268 )}
4369
44- <span class =" post-card-read-more" >{ t (' blog.readMore' )} →</span >
70+ <span class =" post-card-read-more" >
71+ { t (' blog.readMore' )}
72+ <svg class =" read-more-arrow" width =" 16" height =" 16" viewBox =" 0 0 24 24" fill =" none" stroke =" currentColor" stroke-width =" 2" >
73+ <path d =" M5 12h14M12 5l7 7-7 7" />
74+ </svg >
75+ </span >
4576 </div >
4677 </a >
4778</article >
@@ -51,28 +82,89 @@ const categoryLabel = t(`blog.categories.${post.data.category}`);
5182 background: rgba(10, 14, 32, 0.62);
5283 border: 1px solid rgba(240, 246, 255, 0.12);
5384 border-radius: 18px;
54- transition: all 0.3s ease ;
85+ transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1) ;
5586 overflow: hidden;
87+ position: relative;
88+ }
89+
90+ .post-card::before {
91+ content: "";
92+ position: absolute;
93+ inset: -1px;
94+ background: linear-gradient(
95+ 135deg,
96+ rgba(34, 211, 238, 0.4),
97+ rgba(168, 85, 247, 0.3),
98+ rgba(96, 165, 250, 0.3)
99+ );
100+ border-radius: inherit;
101+ z-index: -1;
102+ opacity: 0;
103+ transition: opacity 0.4s cubic-bezier(0.16, 1, 0.3, 1);
56104 }
57105
58106 .post-card:hover {
59- transform: translateY(-4px );
107+ transform: translateY(-6px );
60108 border-color: rgba(34, 211, 238, 0.3);
61- box-shadow: 0 20px 50px rgba(0, 0, 0, 0.4);
109+ box-shadow:
110+ 0 4px 6px rgba(0, 0, 0, 0.1),
111+ 0 10px 20px rgba(0, 0, 0, 0.15),
112+ 0 20px 50px rgba(34, 211, 238, 0.1);
113+ }
114+
115+ .post-card:hover::before {
116+ opacity: 1;
62117 }
63118
64119 .post-card-link {
65120 display: block;
66121 text-decoration: none;
122+ }
123+
124+ /* Hero Image Styles */
125+ .post-card-image {
126+ position: relative;
127+ height: 180px;
128+ overflow: hidden;
129+ }
130+
131+ .post-card-image img {
132+ width: 100%;
133+ height: 100%;
134+ object-fit: cover;
135+ transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
136+ }
137+
138+ .post-card:hover .post-card-image img {
139+ transform: scale(1.08);
140+ }
141+
142+ .post-card-image-overlay {
143+ position: absolute;
144+ inset: 0;
145+ background: linear-gradient(
146+ to bottom,
147+ transparent 0%,
148+ rgba(10, 14, 32, 0.4) 60%,
149+ rgba(10, 14, 32, 0.9) 100%
150+ );
151+ }
152+
153+ .post-card-content {
67154 padding: 1.5rem;
68155 }
69156
157+ .has-hero .post-card-content {
158+ padding-top: 1rem;
159+ }
160+
70161 .post-card-meta {
71162 display: flex;
72163 align-items: center;
73- gap: 1rem ;
164+ gap: 0.5rem ;
74165 margin-bottom: 0.75rem;
75166 font-size: 0.85rem;
167+ flex-wrap: wrap;
76168 }
77169
78170 .post-card-category {
@@ -82,17 +174,32 @@ const categoryLabel = t(`blog.categories.${post.data.category}`);
82174 letter-spacing: 0.05em;
83175 }
84176
177+ .post-card-divider {
178+ color: var(--text-3);
179+ }
180+
85181 .post-card-date {
86182 color: var(--text-3);
87183 }
88184
185+ .post-card-reading-time {
186+ display: inline-flex;
187+ align-items: center;
188+ gap: 0.35rem;
189+ color: var(--text-3);
190+ }
191+
192+ .post-card-reading-time svg {
193+ opacity: 0.7;
194+ }
195+
89196 .post-card-title {
90197 font-size: 1.3rem;
91198 font-weight: 700;
92199 color: var(--text);
93200 margin-bottom: 0.75rem;
94201 line-height: 1.3;
95- transition: color 0.2s ease ;
202+ transition: color 0.3s cubic-bezier(0.16, 1, 0.3, 1) ;
96203 }
97204
98205 .post-card:hover .post-card-title {
@@ -118,19 +225,34 @@ const categoryLabel = t(`blog.categories.${post.data.category}`);
118225 }
119226
120227 .post-card-tag {
121- color: var(--text-3);
228+ background: rgba(168, 85, 247, 0.1);
229+ color: var(--c2);
122230 font-size: 0.8rem;
231+ padding: 0.25rem 0.75rem;
232+ border-radius: 999px;
233+ border: 1px solid rgba(168, 85, 247, 0.2);
234+ transition: all 0.3s ease;
235+ }
236+
237+ .post-card:hover .post-card-tag {
238+ background: rgba(168, 85, 247, 0.15);
239+ border-color: rgba(168, 85, 247, 0.3);
123240 }
124241
125242 .post-card-read-more {
243+ display: inline-flex;
244+ align-items: center;
245+ gap: 0.5rem;
126246 color: var(--c1);
127247 font-size: 0.9rem;
128248 font-weight: 600;
129- transition: transform 0.2s ease;
130- display: inline-block;
131249 }
132250
133- .post-card:hover .post-card-read-more {
251+ .read-more-arrow {
252+ transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
253+ }
254+
255+ .post-card:hover .read-more-arrow {
134256 transform: translateX(4px);
135257 }
136258</style >
0 commit comments