the-grid-cc 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,253 @@
1
+ ---
2
+ cluster: james-weatherhead-blog
3
+ block: 03
4
+ type: execute
5
+ wave: 2
6
+ depends_on: [01]
7
+ files_modified: [src/content/config.ts, src/content/blog/sample-post.md, src/pages/blog/[...slug].astro, src/components/PostList.astro, src/components/Tag.astro]
8
+ autonomous: false
9
+
10
+ must_haves:
11
+ truths:
12
+ - "Blog posts are defined as content collection with schema"
13
+ - "Individual blog posts render with frontmatter data"
14
+ - "Tags are extracted and displayed for each post"
15
+ artifacts:
16
+ - path: "src/content/config.ts"
17
+ provides: "Content collection schema for blog posts"
18
+ exports: ["collections"]
19
+ - path: "src/pages/blog/[...slug].astro"
20
+ provides: "Dynamic blog post page template"
21
+ min_lines: 50
22
+ - path: "src/components/PostList.astro"
23
+ provides: "Reusable component for listing blog posts"
24
+ min_lines: 30
25
+ key_links:
26
+ - from: "src/pages/blog/[...slug].astro"
27
+ to: "getCollection('blog')"
28
+ via: "Astro content collections API"
29
+ pattern: "getCollection\\('blog'\\)"
30
+ - from: "src/content/config.ts"
31
+ to: "zod schema"
32
+ via: "defineCollection"
33
+ pattern: "defineCollection"
34
+ ---
35
+
36
+ <objective>
37
+ Implement blog content structure using Astro Content Collections with markdown posts, frontmatter validation, and tag support.
38
+
39
+ Purpose: Create the core blog functionality - content schema, post rendering, and listing capabilities.
40
+ Output: Content collection configured, sample post created, dynamic post template working.
41
+ </objective>
42
+
43
+ <context>
44
+ Block 01 completed: Astro project initialized with Tailwind and Vercel adapter.
45
+ Block 02 in progress: Layout system (runs parallel with this block).
46
+
47
+ This block focuses on the content layer using Astro's Content Collections API. Requirements specify:
48
+ - Markdown-based posts
49
+ - Tags for organization
50
+ - Syntax highlighting (shiki installed in Block 01)
51
+
52
+ Content Collections provide type-safe frontmatter, automatic routing, and built-in markdown processing. This is the canonical Astro approach for blog content.
53
+
54
+ Block 02 (layouts) and Block 03 (content) can run in parallel since they modify different files with no overlap.
55
+ </context>
56
+
57
+ <threads>
58
+
59
+ <thread type="auto">
60
+ <name>Thread 1: Configure content collection schema</name>
61
+ <files>src/content/config.ts, src/content/blog/sample-post.md</files>
62
+ <action>
63
+ Set up Astro Content Collections for blog posts:
64
+
65
+ 1. Create src/content/config.ts with zod schema:
66
+ ```ts
67
+ import { defineCollection, z } from 'astro:content';
68
+
69
+ const blog = defineCollection({
70
+ type: 'content',
71
+ schema: z.object({
72
+ title: z.string(),
73
+ description: z.string(),
74
+ pubDate: z.coerce.date(),
75
+ updatedDate: z.coerce.date().optional(),
76
+ tags: z.array(z.string()).default([]),
77
+ draft: z.boolean().default(false),
78
+ }),
79
+ });
80
+
81
+ export const collections = { blog };
82
+ ```
83
+
84
+ 2. Create src/content/blog/ directory
85
+
86
+ 3. Create sample post: src/content/blog/sample-post.md
87
+ - Frontmatter with all required fields (title, description, pubDate, tags)
88
+ - Markdown content with headings, paragraphs, code blocks
89
+ - Use tags: ["javascript", "astro", "web-development"]
90
+ - Include code block to test syntax highlighting
91
+
92
+ AVOID: Do not add complex media fields yet (images come later if needed). Do not add author field unless multi-author (single author blog). WHY: Keep schema minimal - only add fields when content requires them.
93
+ </action>
94
+ <verify>
95
+ Run: npm run build (should succeed with no schema errors)
96
+ Check: src/content/config.ts exports 'collections' with 'blog'
97
+ Check: sample-post.md exists with valid frontmatter
98
+ Run: npx astro check (validates content schema)
99
+ </verify>
100
+ <done>
101
+ - src/content/config.ts defines blog collection with zod schema
102
+ - Schema includes: title, description, pubDate, updatedDate, tags, draft
103
+ - sample-post.md created with valid frontmatter
104
+ - Build completes without content validation errors
105
+ </done>
106
+ </thread>
107
+
108
+ <thread type="auto">
109
+ <name>Thread 2: Create dynamic blog post template</name>
110
+ <files>src/pages/blog/[...slug].astro</files>
111
+ <action>
112
+ Create dynamic route for individual blog posts:
113
+
114
+ 1. Create src/pages/blog/[...slug].astro
115
+
116
+ 2. Implement getStaticPaths():
117
+ ```ts
118
+ import { getCollection } from 'astro:content';
119
+ export async function getStaticPaths() {
120
+ const posts = await getCollection('blog', ({ data }) => !data.draft);
121
+ return posts.map(post => ({
122
+ params: { slug: post.slug },
123
+ props: post,
124
+ }));
125
+ }
126
+ ```
127
+
128
+ 3. Destructure props: const { data, render } = Astro.props;
129
+ 4. Render post with BaseLayout:
130
+ - Pass title and description to layout
131
+ - Render metadata: title, date, tags
132
+ - Render content: const { Content } = await render();
133
+ - Style with Tailwind typography: prose dark:prose-invert max-w-3xl
134
+
135
+ 5. Date formatting: use Intl.DateTimeFormat or date-fns
136
+ 6. Tags: render as badges with Tag component (create in next thread)
137
+
138
+ AVOID: Do not add comments (requirements say "none"). Do not add related posts sidebar (keep minimal). WHY: Feature requirements explicitly exclude comments; related posts adds complexity without requirement.
139
+ </action>
140
+ <verify>
141
+ Run: npm run dev
142
+ Visit: http://localhost:4321/blog/sample-post
143
+ Check: Post renders with title, date, tags, and content
144
+ Check: Code blocks have syntax highlighting
145
+ Check: Prose typography applied correctly
146
+ </verify>
147
+ <done>
148
+ - [slug].astro implements getStaticPaths with getCollection
149
+ - Filters out draft posts (data.draft === false)
150
+ - Renders post content with proper typography
151
+ - Displays frontmatter data (title, date, tags)
152
+ - Syntax highlighting works in code blocks
153
+ </done>
154
+ </thread>
155
+
156
+ <thread type="auto">
157
+ <name>Thread 3: Build post list and tag components</name>
158
+ <files>src/components/PostList.astro, src/components/Tag.astro</files>
159
+ <action>
160
+ Create reusable components for displaying posts and tags:
161
+
162
+ PostList.astro:
163
+ 1. Props interface: posts (array of blog collection entries)
164
+ 2. Map over posts and display:
165
+ - Title (link to /blog/{slug})
166
+ - Description
167
+ - Date (formatted)
168
+ - Tags (using Tag component)
169
+ 3. Sort by pubDate descending (newest first)
170
+ 4. Style: space-y-8 for vertical spacing, hover states on titles
171
+ 5. No pagination yet (add later if needed)
172
+
173
+ Tag.astro:
174
+ 1. Props: tag (string), variant?: 'link' | 'badge'
175
+ 2. If variant === 'link': render as link to /blog/tags/{tag}
176
+ 3. If variant === 'badge': render as span with badge styles
177
+ 4. Style: rounded-full px-3 py-1 text-sm bg-gray-200 dark:bg-gray-800
178
+
179
+ AVOID: Do not add filter/search UI (add later if needed). Do not add pagination yet (YAGNI - wait until many posts exist). WHY: Premature optimization - start simple, add features when content volume requires it.
180
+ </action>
181
+ <verify>
182
+ Check: PostList.astro accepts posts prop and maps over entries
183
+ Check: Tag.astro handles both 'link' and 'badge' variants
184
+ Check: Tags are clickable links (variant='link') or static badges
185
+ Run: grep -r "variant=" src/components/Tag.astro
186
+ </verify>
187
+ <done>
188
+ - PostList.astro displays posts with title, description, date, tags
189
+ - Posts sorted by date descending (newest first)
190
+ - Tag.astro renders tags as links or badges based on variant prop
191
+ - Hover states applied to post titles
192
+ </done>
193
+ </thread>
194
+
195
+ <thread type="checkpoint:human-verify" gate="blocking">
196
+ <what-built>
197
+ Complete blog content system with:
198
+ - Content collection schema (src/content/config.ts)
199
+ - Sample blog post (src/content/blog/sample-post.md)
200
+ - Dynamic post template ([slug].astro)
201
+ - PostList component (reusable post listing)
202
+ - Tag component (link and badge variants)
203
+ </what-built>
204
+ <how-to-verify>
205
+ 1. cd /Users/jacweath/grid/blog && npm run dev
206
+ 2. Visit http://localhost:4321/blog/sample-post
207
+ 3. Check post page:
208
+ - Title displays correctly
209
+ - Date shows formatted (e.g., "January 23, 2024")
210
+ - Tags appear as badges or links
211
+ - Markdown content renders properly
212
+ - Code blocks have syntax highlighting (colors visible)
213
+ - Dark mode: check prose-invert class applies (text readable on dark bg)
214
+ 4. Check browser console for errors (should be none)
215
+ 5. Run: npm run build
216
+ - Build should complete successfully
217
+ - Check: npx astro check (no content schema errors)
218
+ 6. Create test listing page:
219
+ - Add src/pages/blog/index.astro using PostList component
220
+ - Visit http://localhost:4321/blog
221
+ - Sample post appears in list
222
+ </how-to-verify>
223
+ <resume-signal>Type "approved" if content renders correctly and schema validates, or describe issues (e.g., "highlighting broken", "schema error", "tags not visible")</resume-signal>
224
+ </thread>
225
+
226
+ </threads>
227
+
228
+ <verification>
229
+ Complete verification checklist:
230
+ 1. src/content/config.ts defines blog collection with zod schema
231
+ 2. Schema includes all required fields (title, description, pubDate, tags, draft)
232
+ 3. src/content/blog/sample-post.md exists with valid frontmatter
233
+ 4. src/pages/blog/[slug].astro implements getStaticPaths correctly
234
+ 5. Dynamic route filters out draft posts
235
+ 6. Post template renders content with prose typography
236
+ 7. Syntax highlighting works in code blocks
237
+ 8. PostList.astro sorts posts by date (newest first)
238
+ 9. Tag.astro supports 'link' and 'badge' variants
239
+ 10. npm run build completes successfully
240
+ 11. npx astro check passes with no errors
241
+ 12. User approves content rendering and functionality
242
+ </verification>
243
+
244
+ <success_criteria>
245
+ - Content collection configured with type-safe schema
246
+ - Sample blog post renders at /blog/sample-post
247
+ - Frontmatter data displays correctly (title, date, tags)
248
+ - Markdown content styled with typography plugin
249
+ - Syntax highlighting functional
250
+ - PostList and Tag components work correctly
251
+ - Build succeeds with no content validation errors
252
+ - User approves rendering and functionality
253
+ </success_criteria>
@@ -0,0 +1,287 @@
1
+ ---
2
+ cluster: james-weatherhead-blog
3
+ block: 04
4
+ type: execute
5
+ wave: 3
6
+ depends_on: [02, 03]
7
+ files_modified: [src/pages/index.astro, src/pages/blog/index.astro, src/pages/about.astro, src/pages/blog/tags/[tag].astro]
8
+ autonomous: false
9
+
10
+ must_haves:
11
+ truths:
12
+ - "Homepage displays recent blog posts"
13
+ - "Blog index page lists all posts"
14
+ - "About page contains author information"
15
+ - "Tag pages filter posts by tag"
16
+ artifacts:
17
+ - path: "src/pages/index.astro"
18
+ provides: "Homepage with recent posts"
19
+ min_lines: 30
20
+ - path: "src/pages/blog/index.astro"
21
+ provides: "Complete blog post listing"
22
+ min_lines: 25
23
+ - path: "src/pages/about.astro"
24
+ provides: "About page with author info"
25
+ min_lines: 20
26
+ - path: "src/pages/blog/tags/[tag].astro"
27
+ provides: "Tag-filtered post listing"
28
+ min_lines: 40
29
+ key_links:
30
+ - from: "index.astro"
31
+ to: "getCollection('blog')"
32
+ via: "fetch recent posts"
33
+ pattern: "getCollection\\('blog'\\)"
34
+ - from: "[tag].astro"
35
+ to: "posts.filter"
36
+ via: "tag filtering"
37
+ pattern: "\\.filter.*tags\\.includes"
38
+ ---
39
+
40
+ <objective>
41
+ Create static pages (homepage, blog index, about) and tag-based filtering functionality.
42
+
43
+ Purpose: Connect layout system (Block 02) and content system (Block 03) into navigable pages with tag-based organization.
44
+ Output: Complete page structure with homepage, blog listing, about page, and tag filtering.
45
+ </objective>
46
+
47
+ <context>
48
+ Dependencies completed:
49
+ - Block 02: Layout system (BaseLayout, Header, Footer, DarkModeToggle)
50
+ - Block 03: Content system (blog collection, post template, PostList component)
51
+
52
+ This block brings everything together into actual pages users will visit. Navigation links from Header (Home, Blog, About) now have destinations. Tag filtering enables content organization without complex search.
53
+
54
+ This is where the blog becomes navigable and complete. After this block, users can:
55
+ - Land on homepage and see recent posts
56
+ - Browse all posts at /blog
57
+ - Read about the author at /about
58
+ - Filter posts by tag at /blog/tags/{tag}
59
+ </context>
60
+
61
+ <threads>
62
+
63
+ <thread type="auto">
64
+ <name>Thread 1: Build homepage and blog index</name>
65
+ <files>src/pages/index.astro, src/pages/blog/index.astro</files>
66
+ <action>
67
+ Create homepage and blog listing pages:
68
+
69
+ index.astro (Homepage):
70
+ 1. Import BaseLayout, PostList, getCollection
71
+ 2. Fetch recent posts:
72
+ ```ts
73
+ const allPosts = await getCollection('blog', ({ data }) => !data.draft);
74
+ const recentPosts = allPosts
75
+ .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf())
76
+ .slice(0, 5); // Show 5 most recent
77
+ ```
78
+ 3. Hero section:
79
+ - Site title: "James Weatherhead"
80
+ - Subtitle/tagline (e.g., "Software Engineer & Technical Writer")
81
+ - Brief intro paragraph (1-2 sentences)
82
+ 4. Recent posts section:
83
+ - Heading: "Recent Posts"
84
+ - Use PostList component with recentPosts
85
+ - Link to /blog: "View all posts →"
86
+ 5. Minimal design: max-w-3xl container, generous whitespace
87
+
88
+ blog/index.astro (Blog Index):
89
+ 1. Import BaseLayout, PostList, getCollection
90
+ 2. Fetch all posts (sorted by date descending)
91
+ 3. Page title: "Blog"
92
+ 4. Optional: display tag cloud (all unique tags)
93
+ 5. Use PostList component with all posts
94
+ 6. Style consistently with homepage
95
+
96
+ AVOID: Do not add complex hero animations (keep minimal). Do not add featured posts section yet (YAGNI). WHY: Requirements specify "minimal design" - focus on content, not embellishments.
97
+ </action>
98
+ <verify>
99
+ Run: npm run dev
100
+ Visit: http://localhost:4321 (homepage)
101
+ Check: Recent posts appear (up to 5)
102
+ Visit: http://localhost:4321/blog (blog index)
103
+ Check: All posts appear in list
104
+ Check: Both pages use BaseLayout with header/footer
105
+ </verify>
106
+ <done>
107
+ - index.astro displays hero section and 5 recent posts
108
+ - blog/index.astro lists all non-draft posts
109
+ - Both pages use consistent styling (max-w-3xl, Tailwind)
110
+ - "View all posts" link navigates to /blog
111
+ - Posts sorted by date descending
112
+ </done>
113
+ </thread>
114
+
115
+ <thread type="auto">
116
+ <name>Thread 2: Create About page</name>
117
+ <files>src/pages/about.astro</files>
118
+ <action>
119
+ Create About page with author information:
120
+
121
+ 1. Create src/pages/about.astro with BaseLayout
122
+ 2. Page title: "About"
123
+ 3. Content structure:
124
+ - Heading: "About James Weatherhead"
125
+ - Bio paragraphs (placeholder text for now - user will customize):
126
+ * Professional background
127
+ * Technical interests
128
+ * Why they write (blog purpose)
129
+ - Contact information (if provided)
130
+ - Optional: Links to GitHub, LinkedIn, Twitter (if provided)
131
+ 4. Style with prose typography: prose dark:prose-invert max-w-3xl
132
+ 5. Consistent container width with other pages
133
+
134
+ Placeholder content example:
135
+ "I'm a software engineer passionate about web development, DevOps, and technical writing. This blog is where I share insights, tutorials, and lessons learned from building software."
136
+
137
+ AVOID: Do not add contact form (YAGNI - add if requested). Do not add photo upload (user can add later). WHY: Keep initial version simple - user can customize content after deployment.
138
+ </action>
139
+ <verify>
140
+ Visit: http://localhost:4321/about
141
+ Check: Page renders with heading and bio content
142
+ Check: Consistent styling with other pages (max-w-3xl, prose)
143
+ Check: Dark mode works correctly on about page
144
+ Check: Header navigation links to /about works
145
+ </verify>
146
+ <done>
147
+ - about.astro created with BaseLayout
148
+ - Page contains heading and placeholder bio content
149
+ - Prose typography applied for readability
150
+ - Consistent container width (max-w-3xl)
151
+ - Navigation link from Header works
152
+ </done>
153
+ </thread>
154
+
155
+ <thread type="auto">
156
+ <name>Thread 3: Implement tag-based filtering</name>
157
+ <files>src/pages/blog/tags/[tag].astro</files>
158
+ <action>
159
+ Create dynamic tag pages for filtering posts by tag:
160
+
161
+ 1. Create src/pages/blog/tags/[tag].astro
162
+
163
+ 2. Implement getStaticPaths to generate page for each unique tag:
164
+ ```ts
165
+ import { getCollection } from 'astro:content';
166
+ export async function getStaticPaths() {
167
+ const allPosts = await getCollection('blog', ({ data }) => !data.draft);
168
+ const uniqueTags = [...new Set(allPosts.flatMap(post => post.data.tags))];
169
+
170
+ return uniqueTags.map(tag => {
171
+ const filteredPosts = allPosts.filter(post =>
172
+ post.data.tags.includes(tag)
173
+ );
174
+ return {
175
+ params: { tag },
176
+ props: { posts: filteredPosts },
177
+ };
178
+ });
179
+ }
180
+ ```
181
+
182
+ 3. Render tag page:
183
+ - Page title: "Posts tagged with '{tag}'"
184
+ - Display tag name prominently
185
+ - Show post count: "X posts"
186
+ - Use PostList component with filtered posts
187
+ - Breadcrumb: "Blog / Tags / {tag}"
188
+ - Link back to /blog
189
+
190
+ 4. Update Tag component (from Block 03):
191
+ - When variant='link', link to /blog/tags/{tag}
192
+ - Ensure tags in PostList are clickable
193
+
194
+ AVOID: Do not add tag description field (YAGNI - tags are self-explanatory). Do not add "related tags" section (over-engineering). WHY: Keep tag pages simple - they're for filtering, not complex taxonomy.
195
+ </action>
196
+ <verify>
197
+ Run: npm run dev
198
+ Visit: http://localhost:4321/blog/tags/javascript (or any tag from sample post)
199
+ Check: Page shows only posts with that tag
200
+ Check: Post count displays correctly
201
+ Check: Tags in PostList are clickable and link to tag pages
202
+ Check: Breadcrumb or back link to /blog exists
203
+ Run: npm run build (should generate static page for each tag)
204
+ </verify>
205
+ <done>
206
+ - [tag].astro implements getStaticPaths with unique tags
207
+ - Filters posts by tag correctly
208
+ - Displays tag name and post count
209
+ - PostList shows filtered posts
210
+ - Tag component links to tag pages (variant='link')
211
+ - Build generates static page for each tag
212
+ </done>
213
+ </thread>
214
+
215
+ <thread type="checkpoint:human-verify" gate="blocking">
216
+ <what-built>
217
+ Complete page structure:
218
+ - Homepage (index.astro) with recent posts
219
+ - Blog index (blog/index.astro) with all posts
220
+ - About page (about.astro) with author info
221
+ - Tag pages (blog/tags/[tag].astro) with filtered posts
222
+ </what-built>
223
+ <how-to-verify>
224
+ 1. cd /Users/jacweath/grid/blog && npm run dev
225
+
226
+ 2. Test Homepage (http://localhost:4321):
227
+ - Hero section displays site title and intro
228
+ - Recent posts section shows up to 5 posts
229
+ - "View all posts" link navigates to /blog
230
+ - Header navigation visible (Home, Blog, About)
231
+
232
+ 3. Test Blog Index (http://localhost:4321/blog):
233
+ - All blog posts listed
234
+ - Posts sorted newest first
235
+ - Tags visible and clickable
236
+
237
+ 4. Test About Page (http://localhost:4321/about):
238
+ - Page renders with author info
239
+ - Consistent styling with other pages
240
+ - Navigation link from Header works
241
+
242
+ 5. Test Tag Filtering:
243
+ - Click a tag from blog index or post page
244
+ - Should navigate to /blog/tags/{tag}
245
+ - Only posts with that tag appear
246
+ - Post count displays correctly
247
+
248
+ 6. Test Navigation:
249
+ - Click each nav link (Home, Blog, About)
250
+ - All pages load without errors
251
+ - Dark mode toggle works on all pages
252
+
253
+ 7. Run: npm run build
254
+ - Build completes successfully
255
+ - Check dist/ contains all static pages
256
+ </how-to-verify>
257
+ <resume-signal>Type "approved" if all pages work and navigation is functional, or describe issues (e.g., "tag filtering broken", "about page missing", "homepage posts not showing")</resume-signal>
258
+ </thread>
259
+
260
+ </threads>
261
+
262
+ <verification>
263
+ Complete verification checklist:
264
+ 1. Homepage renders with hero and recent posts (max 5)
265
+ 2. Blog index lists all non-draft posts sorted by date
266
+ 3. About page displays author information
267
+ 4. Tag pages filter posts correctly by tag
268
+ 5. Navigation links work (Home, Blog, About)
269
+ 6. Tags are clickable and link to tag pages
270
+ 7. Dark mode works consistently across all pages
271
+ 8. All pages use BaseLayout with header/footer
272
+ 9. Responsive on mobile and desktop
273
+ 10. npm run build generates all static pages
274
+ 11. No console errors on any page
275
+ 12. User approves page structure and navigation
276
+ </verification>
277
+
278
+ <success_criteria>
279
+ - Homepage displays recent posts and hero content
280
+ - Blog index shows complete post listing
281
+ - About page contains author information
282
+ - Tag filtering functional (click tag → see filtered posts)
283
+ - All navigation links work correctly
284
+ - Build generates all static pages
285
+ - Consistent styling and dark mode across all pages
286
+ - User approves overall page structure and UX
287
+ </success_criteria>