Skip to content

Commit fe5ea0e

Browse files
committed
Add basic tag page
1 parent 19304ba commit fe5ea0e

File tree

10 files changed

+129
-72
lines changed

10 files changed

+129
-72
lines changed

src/components/PostList.astro

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
---
2+
import type { CollectionEntry } from 'astro:content'
3+
import PostTags from "../components/PostTags.astro";
4+
import * as formatDate from "../util/formatDate";
5+
6+
interface Props {
7+
posts: CollectionEntry<"posts">[],
8+
}
9+
10+
const { posts } = Astro.props;
11+
12+
posts.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf())
13+
---
14+
15+
<div class="collection">
16+
{posts.map((post) => (<>
17+
<p class="date">{formatDate.compact(post.data.pubDate)}</p>
18+
<div class="item">
19+
<p><a href={`/post/${post.id}/`}>
20+
{post.data.title}
21+
{post.data.subtitle &&
22+
<>— {post.data.subtitle}</>
23+
}
24+
</a></p>
25+
{post.data.tags.length > 0 &&
26+
<PostTags tags={post.data.tags} />
27+
}
28+
</div>
29+
</>))}
30+
</div>
31+
32+
<style lang="scss">
33+
@use '../styles/tokens' as *;
34+
35+
.collection {
36+
margin-inline: $space-md;
37+
line-height: $line-height-lg;
38+
text-align: center;
39+
40+
p {
41+
margin-block: 0;
42+
}
43+
44+
.date {
45+
@include font-technical;
46+
text-wrap: nowrap;
47+
}
48+
49+
@media (max-width: $single-width) {
50+
.item:not(:last-child) {
51+
margin-block-end: $size-lg;
52+
}
53+
}
54+
}
55+
56+
@media (min-width: $single-width) {
57+
.collection {
58+
margin-inline: $space-lg;
59+
text-align: left;
60+
61+
display: grid;
62+
gap: $space-md $space-md;
63+
grid-template: auto auto / min-content auto;
64+
65+
.date {
66+
text-align: right;
67+
}
68+
}
69+
}
70+
</style>

src/components/post/Post.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { render, type CollectionEntry } from 'astro:content'
33
import PostHeader from './PostHeader.astro';
44
55
interface Props {
6-
post: CollectionEntry<'blog'>;
6+
post: CollectionEntry<'posts'>;
77
}
88
99
const { post } = Astro.props;

src/components/post/PostHeader.astro

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
import type { InferEntrySchema } from 'astro:content'
33
import { Image } from 'astro:assets';
44
import * as formatDate from '../../util/formatDate';
5-
import TagList from '../TagList.astro';
5+
import PostTags from '../PostTags.astro';
66
import Aside from '../Aside.astro';
77
import Bandcamp from '../Bandcamp.astro';
88
99
interface Props {
10-
data: InferEntrySchema<"blog">;
10+
data: InferEntrySchema<"posts">;
1111
}
1212
1313
const { data } = Astro.props;
@@ -22,7 +22,7 @@ const pubDate = formatDate.verbose(data.pubDate);
2222
<h2>{data.subtitle}</h2>
2323
}
2424
<p>{pubDate}</p>
25-
<TagList tags={data.tags} />
25+
<PostTags tags={data.tags} />
2626
</div>
2727
{data.hero?.image !== undefined &&
2828
<Image

src/content.config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { defineCollection, z } from "astro:content";
22

33
import { glob } from 'astro/loaders';
44

5-
const blog = defineCollection({
5+
const posts = defineCollection({
66
loader: glob({ pattern: '**/*.md', base: './src/content/posts' }),
77
schema: ({ image }) => z.strictObject({
88
title: z.string(),
@@ -22,4 +22,4 @@ const blog = defineCollection({
2222
}),
2323
});
2424

25-
export const collections = { blog };
25+
export const collections = { posts };
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
---
2-
title: 'Walking'
2+
title: 'Blog post'
33
pubDate: 2025-10-23
44
hero:
55
image: '../media/walking-in-woods.jpg'
66
alt: 'A photo of someone walking through woods, taken from a distance behind. There are bright green and yellow leaves.'
7+
tags:
8+
- life
79
---
810

9-
1011
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut non lorem diam. Quisque vulputate nibh sodales eros pretium tincidunt. Aenean porttitor efficitur convallis. Nulla sagittis finibus convallis. Phasellus in fermentum quam, eu egestas tortor. Maecenas ac mollis leo. Integer maximus eu nisl vel sagittis.
1112

1213
# Aliquam ante urna

src/content/posts/example-album.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: "Twain: Noon"
2+
title: "Example"
33
subtitle: Album Review
44
pubDate: 2025-10-28
55
bandcamp:
@@ -8,7 +8,6 @@ bandcamp:
88
tags:
99
- music
1010
- review
11-
- Twain
1211
---
1312

1413
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut non lorem diam. Quisque vulputate nibh sodales eros pretium tincidunt. Aenean porttitor efficitur convallis. Nulla sagittis finibus convallis. Phasellus in fermentum quam, eu egestas tortor. Maecenas ac mollis leo. Integer maximus eu nisl vel sagittis.

src/pages/index.astro

Lines changed: 3 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,12 @@
11
---
22
import { getCollection } from "astro:content";
3+
import PostList from "../components/PostList.astro";
34
import BaseLayout from "../layouts/BaseLayout.astro";
4-
import TagList from "../components/TagList.astro";
5-
import * as formatDate from "../util/formatDate";
65
7-
const posts = await getCollection("blog");
8-
9-
posts.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf())
6+
const posts = await getCollection("posts");
107
---
118

129
<BaseLayout>
1310
<h1>Posts</h1>
14-
<div class="collection">
15-
{posts.map((post) => (<>
16-
<p class="date">{formatDate.compact(post.data.pubDate)}</p>
17-
<div class="item">
18-
<p><a href={`/post/${post.id}/`}>
19-
{post.data.title}
20-
{post.data.subtitle &&
21-
<>— {post.data.subtitle}</>
22-
}
23-
</a></p>
24-
{post.data.tags.length > 0 &&
25-
<TagList tags={post.data.tags} />
26-
}
27-
</div>
28-
</>))}
29-
</div>
11+
<PostList {posts} />
3012
</BaseLayout>
31-
32-
<style lang="scss">
33-
@use '../styles/tokens' as *;
34-
35-
.collection {
36-
margin-inline: $space-md;
37-
line-height: $line-height-lg;
38-
text-align: center;
39-
40-
p {
41-
margin-block: 0;
42-
}
43-
44-
.date {
45-
@include font-technical;
46-
}
47-
48-
@media (max-width: $single-width) {
49-
.item:not(:last-child) {
50-
margin-block-end: $size-lg;
51-
}
52-
}
53-
}
54-
55-
@media (min-width: $single-width) {
56-
.collection {
57-
margin-inline: $space-lg;
58-
text-align: left;
59-
60-
display: grid;
61-
gap: $space-md $space-md;
62-
grid-template: auto auto / auto auto;
63-
64-
.date {
65-
text-align: right;
66-
}
67-
}
68-
}
69-
</style>

src/pages/post/[...slug].astro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import BaseLayout from "../../layouts/BaseLayout.astro";
55
import BlogPost from "../../components/post/Post.astro";
66
77
interface Props {
8-
post: CollectionEntry<"blog">;
8+
post: CollectionEntry<"posts">;
99
}
1010
1111
export const getStaticPaths: GetStaticPaths = async () => {
12-
const posts = await getCollection("blog");
12+
const posts = await getCollection("posts");
1313
return posts.map((post) => ({
1414
params: { slug: post.id },
1515
props: { post },

src/pages/tag/[tag].astro

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
import type { GetStaticPaths } from 'astro';
3+
import type { CollectionEntry } from 'astro:content';
4+
import { getCollection } from 'astro:content';
5+
import BaseLayout from '../../layouts/BaseLayout.astro';
6+
import PostList from '../../components/PostList.astro';
7+
8+
interface Props {
9+
posts: CollectionEntry<"posts">[],
10+
}
11+
12+
export const getStaticPaths: GetStaticPaths = async () => {
13+
const allPosts = await getCollection("posts");
14+
const tags = [...new Set(
15+
allPosts.map(post => post.data.tags).flat()
16+
)];
17+
18+
return tags.map(tag => {
19+
const filteredPosts = allPosts.filter(post => (
20+
post.data.tags.includes(tag)
21+
));
22+
return {
23+
params: { tag },
24+
props: { posts: filteredPosts },
25+
}
26+
});
27+
};
28+
29+
const { tag } = Astro.params;
30+
const { posts } = Astro.props;
31+
---
32+
33+
<BaseLayout>
34+
<h1>Posts tagged ‘{tag}’</h1>
35+
<PostList {posts} />
36+
</BaseLayout>
37+
38+
<style lang="scss">
39+
@use '../../styles/tokens' as *;
40+
41+
.tag {
42+
@include font-technical;
43+
}
44+
</style>

0 commit comments

Comments
 (0)