This Blog Was Built by Claude Code

Why Build Your Own Blog?
There are plenty of places to write. Medium, Tistory, velog — sign up anywhere and you can start immediately. But for a developer, "building your own blog" means something different. Your design, your structure, your features. Not fitting yourself to someone else's platform, but shaping a space that fits your writing.
The problem is time. If you spend all your time building the blog instead of writing, you've got it backwards. So most people pick a platform and give up on customization. A reasonable choice.
But with Claude Code, the story changes a bit.
Starting Point: A Vercel Template
The starting point was Vercel's blog-starter-kit. It's a clean, Next.js-based blog template. Drop a Markdown file in _posts/ and it automatically becomes a post. The core functionality was solid, but the design was straight Vercel branding, there was no dark mode, and no tagging system.
What I wanted was clear:
- A clean design based on shadcn/ui + Tailwind CSS
- Dark mode support
- A tag system for organizing posts
- Syntax highlighting (it's a tech blog, after all)
- Complete removal of all Vercel template traces
Handing the Work to Claude Code
I set the direction, and handed the execution to Claude Code. We broke it into six phases.
Phase 1 — Foundation. Brought in shadcn/ui's CSS variable system, rewrote the Tailwind config, and set fresh global styles. The cn() utility function made conditional class application clean and simple.
Phase 2 — Components. Button, Card, Badge, Avatar, Separator. Generated shadcn/ui components one by one. Since they're Radix UI-based, accessibility came along for free.
Phase 3 — Blog Components. Header, footer, post card, theme toggle. Built the blog-specific components from scratch. For the text-focused post list, I referenced the style from tailwind-nextjs-starter-blog.
Phase 4 — Pages. Home, post detail, About, tag list, tag filter. Rewrote each page to fit the new components and swapped out the markdown pipeline.
Phase 5 — Cleanup. Deleted 15 files that were no longer needed. Sample images, old components, Vercel branding artifacts.
Phase 6 — Verification. Confirmed a successful build and tested every page.
Throughout this entire process, my job was setting direction and making decisions. Things like: "text list instead of a featured hero post," "navigation with just three links: Blog, Tags, About," "single-author blog so no need to show the author on each card." Claude Code wrote the code.
An Interesting Bug: Korean and Bold Text
After building the blog, I published my first post — and the bold text was broken. Patterns like <strong>"text"</strong>는 were rendering with the literal ** characters exposed.
Digging into it, the cause was a quirk of the CommonMark spec. For ** to close a bold span in Markdown, it needs to be recognized as a "right-flanking delimiter." But if the closing ** is immediately preceded by punctuation like a quote or bracket, and immediately followed by a CJK character (like Korean hangul), that condition isn't met.
It's an edge case that almost never comes up in English, specific to CJK characters. The fix was to pre-process the problematic patterns into <strong> HTML tags before the markdown parsing step.
function fixCjkEmphasis(markdown: string): string {
return markdown.replace(
/\*\*(.+?)\*\*(?=[\u3131-\u318E\uAC00-\uD7A3])/g,
"<strong>$1</strong>",
);
}A trap worth knowing about for anyone running a Korean blog on Markdown.
Automatically Generating Cover Images
Does a tech blog need cover images? Honestly, no. But having them clearly makes things look better. The problem is it's tedious to make one every time.
So I automated it. Using the Replicate API to call the Seedream 4.5 model, cover images are generated based on the post's title and summary. The prompt is tuned for blog use:
Minimal, editorial illustration style. Muted tones, soft gradient background.
No text, no letters, no words, no watermark, no UI elements.
Clean composition with generous negative space.
Suitable as a blog hero image at 16:9 aspect ratio.
Minimal, no text, generous white space. Perfect for a blog hero image.
I took it a step further and built a pipeline with GitHub Actions. Push a new post to the _posts/ directory, and it automatically detects posts without cover images, generates them, and commits them.
on:
push:
branches: [main]
paths:
- "_posts/**"Just write the post. The image takes care of itself.
A Meta Observation
I'm writing this post alongside Claude Code too. Using the same tool that built this blog to write about building this blog.
Looking back, the most time-consuming part of the whole process wasn't the coding. It was deciding what kind of blog to build. Design direction, feature scope, technology choices. Those decisions still belong to the person.
But once the decision is made, execution is fast. Setting up shadcn/ui components, rewriting pages, swapping the markdown pipeline, adding the tag system, automating cover image generation — all of this happened within a single conversation.
In the old days, this would have been a full weekend of work, and I'd have gotten through maybe half of it. Now, the moment an idea strikes, I can act on it immediately. And the result of that execution is what I'm writing on right now.
The problem of building a blog instead of writing for it. Solved.