Skip to content

Commit bce34a8

Browse files
committed
feat: implement articles page with pagination and enhanced layout
1 parent 23693f0 commit bce34a8

1 file changed

Lines changed: 344 additions & 1 deletion

File tree

src/pages/articles/index.astro

Lines changed: 344 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,346 @@
11
---
2-
return Astro.redirect("/articles/page/1/");
2+
import { Image } from "astro:assets";
3+
import { getCollection } from "astro:content";
4+
import BaseHead from "../../components/BaseHead.astro";
5+
import Footer from "../../components/Footer.astro";
6+
import FormattedDate from "../../components/FormattedDate.astro";
7+
import Header from "../../components/Header.astro";
8+
import ArwesBackground from '../../components/ArwesBackground';
9+
import Pagination from "../../components/Pagination.astro";
10+
import { SITE_DESCRIPTION, SITE_TITLE } from "../../consts";
11+
12+
const posts = (await getCollection("blog")).sort(
13+
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf()
14+
);
15+
16+
const pageSize = 6;
17+
const totalPages = Math.ceil(posts.length / pageSize);
18+
const currentPageData = posts.slice(0, pageSize);
19+
20+
const page = {
21+
data: currentPageData,
22+
start: 0,
23+
end: Math.min(pageSize - 1, posts.length - 1),
24+
size: pageSize,
25+
total: posts.length,
26+
currentPage: 1,
27+
lastPage: totalPages,
28+
url: {
29+
current: "/articles/",
30+
prev: undefined,
31+
next: totalPages > 1 ? "/articles/page/2/" : undefined,
32+
first: "/articles/",
33+
last: `/articles/page/${totalPages}/`
34+
}
35+
};
336
---
37+
38+
<!doctype html>
39+
<html lang="en">
40+
<head>
41+
<BaseHead
42+
title={SITE_TITLE}
43+
description={SITE_DESCRIPTION}
44+
/>
45+
</head>
46+
<body>
47+
<ArwesBackground client:load />
48+
<div style="position: relative; z-index: 1;">
49+
<Header />
50+
<main class="articles-main">
51+
<!-- Hero Section -->
52+
<section class="hero-section">
53+
<h1 class="hero-title">
54+
<span class="title-bracket">&gt;&gt;</span> Articles
55+
</h1>
56+
<p class="hero-description">
57+
Thoughts, insights, and tutorials on software development,
58+
architecture, and technology. Explore my latest writings and
59+
discoveries.
60+
</p>
61+
<div class="hero-divider"></div>
62+
</section>
63+
64+
<!-- Articles Grid -->
65+
<section class="articles-section">
66+
<div class="articles-grid">
67+
{
68+
page.data.map((post) => (
69+
<article class="article-card">
70+
<div class="card-border-top"></div>
71+
<a
72+
href={`/articles/${post.id}/`}
73+
class="card-link"
74+
rel="prefetch"
75+
>
76+
{post.data.heroImage && (
77+
<div class="card-image-wrapper">
78+
<Image
79+
width={720}
80+
height={360}
81+
src={post.data.heroImage}
82+
alt={post.data.title}
83+
class="card-image"
84+
/>
85+
<div class="image-overlay" />
86+
</div>
87+
)}
88+
<div class="card-content">
89+
<div class="card-body">
90+
<h3 class="card-title">
91+
<span class="title-arrow">&gt;</span> {post.data.title}
92+
</h3>
93+
<p class="card-description">{post.data.description}</p>
94+
</div>
95+
<div class="card-footer">
96+
<FormattedDate date={post.data.pubDate} />
97+
<span class="read-more">
98+
Read more <span class="arrow">→</span>
99+
</span>
100+
</div>
101+
</div>
102+
</a>
103+
<div class="card-border-bottom"></div>
104+
</article>
105+
))
106+
}
107+
</div>
108+
109+
<!-- Pagination -->
110+
<Pagination page={page} baseUrl="/articles" />
111+
</section>
112+
</main>
113+
<Footer />
114+
</div>
115+
</body>
116+
</html>
117+
118+
<style>
119+
.articles-main {
120+
width: 100%;
121+
padding: 3rem 0;
122+
}
123+
124+
.hero-section {
125+
text-align: center;
126+
margin-bottom: 4rem;
127+
padding: 0 1rem;
128+
}
129+
130+
.hero-title {
131+
font-size: 2.5rem;
132+
font-weight: bold;
133+
color: var(--color-text-primary);
134+
margin-bottom: 1.5rem;
135+
text-shadow: 0 0 15px rgba(0, 217, 255, 0.5);
136+
}
137+
138+
.page-indicator {
139+
font-size: 1.5rem;
140+
color: var(--color-text-secondary);
141+
font-weight: normal;
142+
}
143+
144+
.title-bracket {
145+
color: var(--color-accent);
146+
margin-right: 0.5rem;
147+
}
148+
149+
@media (min-width: 768px) {
150+
.hero-title {
151+
font-size: 3rem;
152+
}
153+
}
154+
155+
.hero-description {
156+
font-size: 1.125rem;
157+
color: var(--color-text-body);
158+
max-width: 48rem;
159+
margin: 0 auto;
160+
line-height: 1.75;
161+
}
162+
163+
.hero-divider {
164+
width: 6rem;
165+
height: 2px;
166+
background: linear-gradient(
167+
90deg,
168+
transparent 0%,
169+
var(--color-primary) 50%,
170+
transparent 100%
171+
);
172+
margin: 2rem auto 0;
173+
border-radius: 9999px;
174+
}
175+
176+
.articles-section {
177+
padding: 0 1rem;
178+
max-width: 1200px;
179+
margin: 0 auto;
180+
}
181+
182+
.articles-grid {
183+
display: grid;
184+
grid-template-columns: repeat(2, 1fr);
185+
gap: 2rem;
186+
}
187+
188+
@media (max-width: 768px) {
189+
.articles-grid {
190+
grid-template-columns: 1fr;
191+
gap: 2.5rem;
192+
}
193+
}
194+
195+
.article-card {
196+
position: relative;
197+
background: var(--color-bg-secondary);
198+
border: 1px solid var(--color-border-dim);
199+
border-radius: 8px;
200+
overflow: hidden;
201+
transition: all var(--duration-normal) ease;
202+
}
203+
204+
.article-card:hover {
205+
transform: translateY(-8px);
206+
border-color: var(--color-primary);
207+
box-shadow: 0 20px 40px rgba(0, 217, 255, 0.2);
208+
}
209+
210+
.card-border-top,
211+
.card-border-bottom {
212+
position: absolute;
213+
left: 0;
214+
right: 0;
215+
height: 2px;
216+
background: linear-gradient(90deg, var(--color-primary), var(--color-accent));
217+
opacity: 0;
218+
transition: opacity var(--duration-normal) ease;
219+
}
220+
221+
.card-border-top {
222+
top: 0;
223+
}
224+
225+
.card-border-bottom {
226+
bottom: 0;
227+
}
228+
229+
.article-card:hover .card-border-top,
230+
.article-card:hover .card-border-bottom {
231+
opacity: 1;
232+
}
233+
234+
.card-link {
235+
display: block;
236+
text-decoration: none;
237+
color: inherit;
238+
height: 100%;
239+
}
240+
241+
.card-image-wrapper {
242+
position: relative;
243+
aspect-ratio: 2 / 1;
244+
overflow: hidden;
245+
}
246+
247+
.card-image {
248+
width: 100%;
249+
height: 100%;
250+
object-fit: cover;
251+
transition: transform var(--duration-normal) ease;
252+
}
253+
254+
.image-overlay {
255+
position: absolute;
256+
inset: 0;
257+
background: linear-gradient(
258+
135deg,
259+
rgba(0, 217, 255, 0.1) 0%,
260+
transparent 50%,
261+
rgba(0, 217, 255, 0.05) 100%
262+
);
263+
opacity: 0;
264+
transition: opacity var(--duration-normal) ease;
265+
}
266+
267+
.article-card:hover .card-image {
268+
transform: scale(1.05);
269+
}
270+
271+
.article-card:hover .image-overlay {
272+
opacity: 1;
273+
}
274+
275+
.card-content {
276+
padding: 1.5rem;
277+
display: flex;
278+
flex-direction: column;
279+
justify-content: space-between;
280+
height: calc(100% - 180px);
281+
}
282+
283+
.card-title {
284+
font-size: 1.25rem;
285+
font-weight: 600;
286+
color: var(--color-text-primary);
287+
margin-bottom: 0.75rem;
288+
line-height: 1.4;
289+
transition: color var(--duration-normal) ease;
290+
}
291+
292+
.title-arrow {
293+
color: var(--color-accent);
294+
font-weight: bold;
295+
margin-right: 0.5rem;
296+
transition: transform var(--duration-normal) ease;
297+
}
298+
299+
.article-card:hover .title-arrow {
300+
transform: translateX(4px);
301+
}
302+
303+
.article-card:hover .card-title {
304+
color: var(--color-accent);
305+
}
306+
307+
.card-description {
308+
color: var(--color-text-body);
309+
line-height: 1.6;
310+
margin-bottom: 1rem;
311+
flex-grow: 1;
312+
}
313+
314+
.card-footer {
315+
display: flex;
316+
align-items: center;
317+
justify-content: space-between;
318+
font-size: 0.875rem;
319+
}
320+
321+
.card-date {
322+
color: var(--color-text-secondary);
323+
font-weight: 500;
324+
font-family: var(--font-family-mono);
325+
}
326+
327+
.read-more {
328+
color: var(--color-accent);
329+
font-weight: 500;
330+
transition: all var(--duration-normal) ease;
331+
}
332+
333+
.article-card:hover .read-more {
334+
color: var(--color-primary);
335+
text-shadow: 0 0 8px rgba(0, 217, 255, 0.6);
336+
}
337+
338+
.arrow {
339+
display: inline-block;
340+
transition: transform var(--duration-normal) ease;
341+
}
342+
343+
.article-card:hover .arrow {
344+
transform: translateX(4px);
345+
}
346+
</style>

0 commit comments

Comments
 (0)