moved blog posts out of content.ts for external management
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import blogPosts from "./blog-posts.json";
|
||||
|
||||
|
||||
export type MoreSection = {
|
||||
title?: string;
|
||||
@@ -221,85 +223,13 @@ A real-world solution for event staff and volunteer management.
|
||||
}];
|
||||
|
||||
|
||||
export const BLOG_POSTS: BlogPost[] = [
|
||||
{
|
||||
id: 1,
|
||||
slug: "adding-a-blog-tab",
|
||||
title: "Adding a blog tab to a portfolio site",
|
||||
description: "A portfolio is a static-ish site with a lot of moving parts. Here's how I added a fourth tab to this very site without adding a single dependency.",
|
||||
date: "2026-10-26",
|
||||
category: "development",
|
||||
tags: ["next.js", "react", "typescript"],
|
||||
details: `# Adding a blog tab to a portfolio site
|
||||
|
||||
This little corner of the site started as a question: **how does long-form content fit into a site that was built for short-form stuff?** Portfolio sites are grids of cards, and a blog is a column of words. The answer turned out to be \"don't treat them differently at all.\"
|
||||
|
||||
<blockquote>
|
||||
A blog is just another collection of cards. The difference is what happens when you click one.
|
||||
</blockquote>
|
||||
|
||||
## The shape of the problem
|
||||
|
||||
Every tab on this site follows the same recipe:
|
||||
|
||||
- A typed data array in a single content file, which acts as the whole CMS
|
||||
- A shared layout that provides the nav chrome and the footer
|
||||
- A thin client page that renders one list component
|
||||
- A detail page that reuses the same layout, minus the nav, plus a context provider
|
||||
|
||||
A post that fits that shape is just another entry in an array. A post that doesn't fit it is a content management system in disguise, and that's a whole other project.
|
||||
|
||||
## Why the markdown was already there
|
||||
|
||||
The project detail pages render markdown through a small remark pipeline that has been sitting in the codebase for a while, used by the project pages. The blog needed exactly the same thing, which meant the data layer for a blog post is mostly just a type and a lookup function. The pipeline is shared, so a post and a project detail are rendered by the same code, with the same quirks, and the same fixes.
|
||||
|
||||
That is the whole trick. The feature felt like it would need infrastructure, but the infrastructure already existed for a different feature. When a new feature turns out to be a clone of an old one, the new feature is cheap, and the codebase gets a little more regular as a side effect.
|
||||
|
||||
## What actually changed
|
||||
|
||||
A new type, a new data array, a new lookup function, a new tab in the nav, two layouts, a list page, a card, and a detail page. No new dependencies, no new build steps, no new configuration. The tab order now reads profile, project, blog, mods, and the blog sits in the middle of the two portfolio tabs.
|
||||
|
||||
Editing a post from here on out is editing the array, which is the same as editing a project. That's the part I wanted.
|
||||
`,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
slug: "hello-world",
|
||||
title: "Hello, world. This is the new blog.",
|
||||
description: "A short note about why this site grew a fourth tab, how the posts are written, and what the rest of this page demonstrates.",
|
||||
date: "2026-09-14",
|
||||
category: "meta",
|
||||
tags: ["meta", "blog"],
|
||||
details: `# Hello, world. This is the new blog.
|
||||
|
||||
This is a blog now. There's a fourth tab up top, and it works the same way the rest of the site works: content lives in the repository as plain data, the page renders it, and the markdown is processed by the same pipeline that renders the project detail pages.
|
||||
|
||||
<blockquote>
|
||||
If a site is only ever going to have three tabs, it's a portfolio. The fourth tab is where it starts being a site.
|
||||
</blockquote>
|
||||
|
||||
## Why it exists
|
||||
|
||||
The portfolio is for finished things, and the mods page is for the things I ship to other people's games. This is for the middle: write-ups, notes, and the occasional \"here's how I did that\" that doesn't belong anywhere else. It's also the first place on this site where I write in the first person at length, which is a change.
|
||||
|
||||
## How posts are written
|
||||
|
||||
Each post is a typed object in the content file, with a slug, a date, a short description for the card, and a markdown body. The body goes through the site's existing remark pipeline, so any markdown that works on the project pages works here, including raw HTML blocks when I want a specific layout.
|
||||
|
||||
- Headings render in the mono face, like the rest of the site
|
||||
- Blockquotes get the raised, bordered treatment you can see above
|
||||
- Images are kept out of this post on purpose. The pipeline supports them, and a future post will use one
|
||||
|
||||
## What this page is doing while you read it
|
||||
|
||||
This page is a dynamic route. The slug comes from the URL, the layout resolves it to rendered HTML on the server, and the page on the client side reads that and paints it in. That's the same architecture the project detail pages use, which is the point of reusing it: a new page type that can't do anything the old page types can't already do.
|
||||
`,
|
||||
}
|
||||
];
|
||||
/* Blog posts live in blog-posts.json so tooling (blog-manager/) can edit them
|
||||
* without rewriting TypeScript. The shape must match the BlogPost type above. */
|
||||
export const BLOG_POSTS: BlogPost[] = blogPosts as BlogPost[];
|
||||
|
||||
/* ---- blog directory helpers ----
|
||||
* Pagination and category filtering for the /blog list. Everything derives
|
||||
* from BLOG_POSTS above, which remains the single source of truth. */
|
||||
* from BLOG_POSTS (data/blog-posts.json), the single source of truth. */
|
||||
|
||||
/** Small enough that pagination is visible as soon as a few posts exist. */
|
||||
export const POSTS_PER_PAGE = 3;
|
||||
|
||||
Reference in New Issue
Block a user