feat(timeline): added timeline page

fix tags page style
This commit is contained in:
ClovertaTheTrilobita 2026-03-25 10:12:49 +02:00
parent a0c13634f9
commit b10f60754a
19 changed files with 305 additions and 20 deletions

View file

@ -9,8 +9,6 @@ image:
tags: ["astro", "blogging", "learning in public", "Hello"]
---
Published on: 2022-07-01
Welcome to my _new blog_ about learning Astro! Here, I will share my learning journey as I build a new website.
## What I've accomplished

View file

@ -6,11 +6,9 @@ author: 'Astro Learner'
image:
url: 'https://docs.astro.build/assets/rose.webp'
alt: 'The Astro logo on a dark background with a pink glow.'
tags: ["astro", "blogging", "learning in public", "Hello"]
tags: ["astro", "blogging", "learning in public"]
---
Published on: 2022-07-01
Welcome to my _new blog_ about learning Astro! Here, I will share my learning journey as I build a new website.
## What I've accomplished

View file

@ -9,6 +9,7 @@ const t = getTranslations(lang);
<a href={`/${lang}`}>{t.nav.home}</a>
<a href={`/${lang}/about`}>{t.nav.about}</a>
<a href={`/${lang}/tags`}>{t.nav.tags}</a>
<a href={`/${lang}/timeline`}>{t.nav.timeline}</a>
</nav>
<style>

View file

@ -6,6 +6,7 @@ const data = Astro.props;
<a href={data.url} class="post-link">
<div class="post-text">
<span class="post-title">{data.title}</span>
<span class="post-description">{data.description}</span>
<span class="post-date">{data.date}</span>
</div>
@ -70,6 +71,16 @@ const data = Astro.props;
display: block;
}
.post-description {
color: black;
font-weight: 400;
font-style: italic;
}
:global(.dark) .post-description {
color: #e6e6e6;
}
@media (max-width: 640px) {
.post-link {
gap: 0.75rem;

View file

@ -19,6 +19,7 @@ const allPosts = await getCollection("blog");
<PostItem
url={`/${lang}/posts/${post.id}/`}
title={post.data.title}
description={post.data.description}
date={formattedDate}
img={post.data.image.url}
/>

View file

@ -0,0 +1,182 @@
---
import { getCollection } from "astro:content";
import { getLangFromUrl, getTranslations } from "@/i18n";
const lang = getLangFromUrl(Astro.url);
const t = getTranslations(lang);
const allPosts = await getCollection("blog");
const sortedPosts = [...allPosts].sort(
(a, b) =>
new Date(b.data.pubDate).getTime() - new Date(a.data.pubDate).getTime(),
);
const groupedPosts = sortedPosts.reduce((acc: any[], post: any) => {
const month = new Date(post.data.pubDate).toISOString().slice(0, 7);
const lastGroup = acc[acc.length - 1];
if (lastGroup && lastGroup.month === month) {
lastGroup.posts.push(post);
} else {
acc.push({
month,
posts: [post],
});
}
return acc;
}, []);
---
<ul class="timeline">
{
groupedPosts.map((group) => (
<li class="timeline-item">
<div class="timeline-node" aria-hidden="true" />
<div class="timeline-group">
<p class="post-date">{group.month}</p>
<div class="month-posts">
{group.posts.map((post: any) => (
<a
href={`/${lang}/posts/${post.id}/`}
class="timeline-card"
>
<h2 class="post-title">{post.data.title}</h2>
<p class="post-description">
{post.data.description}
</p>
</a>
))}
</div>
</div>
</li>
))
}
</ul>
<style>
.timeline {
list-style: none;
margin: 0;
padding: 0 0 0 1.6rem;
position: relative;
}
.timeline::before {
content: "";
position: absolute;
top: 0.2rem;
bottom: 0.2rem;
left: 0.4rem;
width: 2px;
background: #9aa7b3;
}
.timeline-item {
position: relative;
margin: 0 0 1.8rem 0;
padding-left: 1.2rem;
}
.timeline-node {
position: absolute;
left: calc(0.4rem - 0.25rem + 1px);
top: 0.75rem;
width: 0.5rem;
height: 0.5rem;
border-radius: 50%;
background: #8e8e8e;
z-index: 1;
}
.timeline-group {
min-width: 0;
}
.post-date {
margin: 0 0 0.6rem 0;
font-size: 1.5rem;
font-weight: 700;
line-height: 1.4;
color: #6f8090;
}
.month-posts {
display: flex;
flex-direction: column;
gap: 0.8rem;
}
.timeline-card {
display: flex;
flex-direction: column;
gap: 0.2rem;
text-decoration: none;
color: inherit;
min-width: 0;
}
.post-title {
margin: 0;
font-size: 1.05rem;
font-weight: 700;
line-height: 1.5;
color: inherit;
overflow-wrap: anywhere;
word-break: break-word;
}
.post-description {
margin: 0;
font-size: 0.88rem;
color: #555;
font-style: italic;
line-height: 1.5;
overflow-wrap: anywhere;
word-break: break-word;
}
.timeline-card:hover .post-title {
text-decoration: underline;
}
:global(.dark) .timeline::before {
background: #6f8090;
}
:global(.dark) .timeline-node {
background: #9a9a9a;
}
:global(.dark) .post-date {
color: #aab7c4;
}
:global(.dark) .post-description {
color: #c8c8c8;
}
@media (max-width: 640px) {
.timeline {
padding-left: 1.3rem;
}
.timeline-item {
padding-left: 1rem;
}
.post-date {
font-size: 1.1rem;
}
.post-title {
font-size: 1rem;
}
.post-description {
font-size: 0.84rem;
}
}
</style>

View file

@ -8,6 +8,7 @@ export default {
posts: "Posts",
tags: "Tags",
about: "About",
timeline: "timeline",
},
post: {
writtenBy: "Written by",

View file

@ -8,6 +8,7 @@ export default {
posts: "文章",
tags: "标签",
about: "关于",
timeline: "时间轴"
},
post: {
writtenBy: "作者",
@ -46,7 +47,7 @@ export default {
},
tags: {
title: "标签",
description: "在这里收集着整篇博客出现过的标签,点击标签跳转对应的文章列表。"
description: "在这里收集着整篇博客出现过的标签,点击标签跳转对应的文章列表。",
},
footer: {
githubIntro: '在 <a href="https://www.github.com/ClovertaTheTrilobita">GitHub</a> 查看更多!',

View file

@ -36,7 +36,7 @@ const { pageTitle } = Astro.props;
::view-transition-old(page),
::view-transition-new(page) {
animation-duration: 0.35s;
animation-duration: 0.05s;
animation-timing-function: ease;
}

View file

@ -37,6 +37,8 @@ const t = getTranslations(lang);
/>
</div>
<div class="post-divider"></div>
<slot />
<Giscus />
@ -91,6 +93,15 @@ const t = getTranslations(lang);
display: block;
}
.post-divider {
border-top: 1px dashed #dfe4e9;
margin: 1.2rem 0 1.5rem;
}
:global(.dark) .post-divider {
border-top-color: #7f91a3;
}
.tags {
display: flex;
flex-wrap: wrap;

View file

@ -3,6 +3,7 @@ import { getCollection } from "astro:content";
import BaseLayout from "@/layouts/BaseLayout.astro";
import PostItem from "@/components/Posts/PostItem.astro";
import { getTranslations, type Lang } from "@/i18n";
import "@/styles/global.css";
export async function getStaticPaths() {
const allPosts = await getCollection("blog");
@ -31,10 +32,55 @@ const t = getTranslations(lang);
---
<BaseLayout pageTitle={String(tag)}>
<p>
{lang === "zh" ? `带有标签 ${tag} 的文章` : `Posts tagged with ${tag}`}
<p class="tag-heading">
{lang === "zh" ? "带有标签" : "Posts tagged with"}{" "}
<span class="tag-chip">{tag}</span>
{lang === "zh" ? "的文章" : ""}
</p>
<ul>
{posts.map((post) => <PostItem post={post} lang={lang} />)}
{
posts.map((post: any) => {
const formattedDate = new Date(post.data.pubDate)
.toISOString()
.split("T")[0];
return (
<PostItem
url={`/${lang}/posts/${post.id}/`}
title={post.data.title}
date={formattedDate}
img={post.data.image.url}
/>
);
})
}
</ul>
</BaseLayout>
<style>
.tag-heading {
margin: 0 0 1rem 0;
}
.tag-chip {
display: inline-block;
padding: 0.22em 0.65em;
font-size: 0.88rem;
line-height: 1.2;
border: 1px dashed #8b6b4a;
border-radius: 0;
background-color: transparent;
color: #6f4e37;
vertical-align: middle;
}
:global(.dark) .tag-chip {
border-color: #d8c7a1;
color: #e6d8b8;
}
ul {
margin: 0;
padding-left: 0;
}
</style>

View file

@ -18,6 +18,7 @@ const pageTitle = lang === "zh" ? "标签索引" : "Tag Index";
<BaseLayout pageTitle={pageTitle}>
<h1>{t.tags.title}</h1>
<p>{t.tags.description}</p>
<div class="tags">
{
tags.map((tag) => (
@ -30,21 +31,37 @@ const pageTitle = lang === "zh" ? "标签索引" : "Tag Index";
</BaseLayout>
<style>
a {
color: #00539f;
}
.tags {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
margin-top: 0.8rem;
}
.tag {
margin: 0.25em;
border: dotted 1px #a1a1a1;
border-radius: 0.5em;
padding: 0.5em 1em;
font-size: 1.15em;
background-color: #f8fcfd;
margin: 0;
padding: 0.22em 0.65em;
font-size: 1rem;
line-height: 1.2;
border: 1px dashed #8b6b4a;
border-radius: 0;
background-color: transparent;
}
.tag a {
color: #6f4e37;
text-decoration: none;
}
.tag a:hover {
text-decoration: underline;
}
:global(.dark) .tag {
border-color: #d8c7a1;
}
:global(.dark) .tag a {
color: #e6d8b8;
}
</style>

View file

@ -0,0 +1,18 @@
---
import BaseLayout from "@/layouts/BaseLayout.astro";
import PostTimeline from "@/components/Posts/PostTimeline.astro";
import { getLangFromUrl, getTranslations } from "@/i18n";
import "@/styles/global.css";
export function getStaticPaths() {
return [{ params: { lang: "zh" } }, { params: { lang: "en" } }];
}
const lang = getLangFromUrl(Astro.url);
const t = getTranslations(lang);
const headerTitle = lang === "zh" ? "时间轴" : "Timeline";
---
<BaseLayout pageTitle={headerTitle}>
<PostTimeline />
</BaseLayout>