Euge's blog

Vibe-code your own SSG

Stop wrestling with frameworks. Vibe code your own lean static site generator.

Published on 2025-06-01 | Tags: vibe-code, ssg, python, minimalist, DIY

So you want a simple blog. Just a place to jot down your experiments, share some thoughts. Static HTML is the obvious, robust choice. But writing raw HTML like it's 1999? Nah. You want Markdown, maybe a sprinkle of templating, a way to manage posts without pulling your hair out.

The usual suspects – Jekyll, Astro, Hugo – they're powerful, sure. But they can also feel like bringing a bazooka to a knife fight. Dependencies, complex configurations, a whole ecosystem to learn. What if you just want to get your words out there, with minimal fuss?

I hit this point recently and decided to "vibe-code" my own Static Site Generator (SSG). Turns out, it's surprisingly straightforward and a great way to understand what these tools actually do. Forget the bloat; let's vibe-code something lean.

Here's the bare-bones recipe

0. Programming Language: Your Choice

Honestly, pick your poison. Python, Node.js, Ruby, even a shell script if you're feeling particularly masochistic. The core logic is simple enough for any modern language. I went with Python because its standard library is packed with goodies for file manipulation, and excellent libraries for Markdown and templating (like Markdown and Jinja2) are a pip install away. The key is picking something you're comfortable with and that won't get in your way.

1. The Generation Script: Your SSG's Heart

This is where the magic happens. At its core, your script will:

  • Scan for content: Find all your Markdown files (your blog posts).
  • Parse metadata: Extract frontmatter (title, date, tags, etc.) from each post. YAML or JSON are common choices here. Most Markdown libraries can handle this.
  • Convert Markdown to HTML: Transform your post content into web-friendly HTML.
  • Apply templates: Inject the generated HTML and metadata into your base HTML templates (e.g., one for a single post, one for the homepage).
  • Write output: Save the final HTML files to a designated output directory (often dist or public).

Keep it simple. You don't need a complex plugin architecture for version 0.1. Focus on the core transformation pipeline.

2. Templates Folder: The Skeleton of Your Site

These are your HTML blueprints. You'll likely want at least:

  • base.html: The main site structure (header, footer, navigation). Other templates will extend this.
  • post.html: How a single blog post is displayed.
  • index.html (or home.html): Your homepage, probably listing recent posts.

Templating engines like Jinja2 (Python), Handlebars (JavaScript), or Liquid (Ruby, and what Jekyll uses) are your friends here. They let you use variables, loops, and includes to keep your HTML DRY (Don't Repeat Yourself).

Example post.html (Jinja2-ish):

{% extends "base.html" %}
{% block title %}{{ post.title }}{% endblock %}
{% block content %}
  <h1>{{ post.title }}</h1>
  <p class="date">Published on: {{ post.date }}</p>
  <div class="post-content">
    {{ post.content_html | safe }}
  </div>
{% endblock %}

3. Assets Folder: The Style and Flair

This is where your CSS, JavaScript (if any), images, and fonts reside. Your generation script will likely just copy these files directly into the output directory, maintaining their structure. Start with a single style.css. You can always add more later. Resist the urge to install a massive CSS framework unless you really need it. A few well-crafted CSS rules can go a long way.

4. Posts Folder: Your Content Hub

A straightforward directory where each .md file is a blog post. A common convention is to name files like YYYY-MM-DD-your-post-slug.md. The frontmatter at the top of each file is key for your generation script.

Example post (2025-06-01-my-first-post.md):

---
title: "My First Awesome Post"
date: 2025-06-01
tags: [introduction, exciting-stuff]
---

Hello world! This is my first post, generated by **my own** SSG.
It feels good.

5. The "Extra Support" Goodies: Because Details Matter

Once you have the basics, these aren't hard to add and make your site a better web citizen:

  • atom.xml / rss.xml: An XML feed for aggregators. Your script can generate this by looping through your posts.
  • robots.txt: Tells search engine crawlers what they can and cannot index.
  • sitemap.xml: Helps search engines discover all the pages on your site.
  • CNAME (if using a custom domain with services like GitHub Pages): A file containing just your custom domain name.
  • llms.txt (optional, emerging): If you want to serve your site as context for LLMs.

Bonus: Kickstart with an LLM

Feeling lazy or just want a quick starting point? Modern LLMs are surprisingly good at bootstrapping simple scripts. Try a prompt like this (tailor it to your preferences):

Create a simple static site generator in Python. It should:

1. Read all `.md` files from a `posts` directory.
2. Parse YAML frontmatter (title, date) from each file.
3. Convert Markdown content to HTML.
4. Use Jinja2 templates from a `templates` directory: `base.html` and `post.html`.
5. `base.html` should define blocks for `title` and `content`. It should link to a `style.css` file.
6. `post.html` should extend `base.html` and display the post title and content.
7. Generate an `index.html` in a `dist` directory, listing titles and links to all posts, sorted by date (newest first).
8. Generate individual HTML files for each post in the `dist` directory (e.g., `dist/my-post-slug.html`).
9. Copy an `assets` directory (which should contain the `style.css`) to `dist/assets`.
10. The `style.css` should implement a clean, minimalist, responsive design with a dark theme. Use a sans-serif font for readability.

Provide the Python script, example minimal `base.html` and `post.html` templates, and a basic `style.css`.

Pro tip: This works even better with AI IDEs like Cursor, Windsurf, or Cline rather than standalone LLMs. These tools can actually create the entire file structure for you automatically – the Python script, template files, CSS, even a sample blog post to test with. No copy-pasting required.

It may not give you a production-ready SSG, but it's a fantastic V0.0.1. You can then iterate, refactor, and add features as you see fit, truly making it your own.

P.S. I used Cursor with Claude 4 Sonnet for the initial version, and it handled this prompt beautifully – complete with working templates and surprisingly decent CSS.

The Payoff? Control and Understanding.

Building your own SSG isn't just a technical exercise. It gives you complete control over your site, zero opaque dependencies, and a deeper understanding of how web content is structured and served. When something breaks, you know exactly where to look (or ask the LLM to do so). When you want a new feature, you're not fighting a framework; you're just adding a bit more code to your script.

So, before you reach for that heavy-duty SSG framework, ask yourself: could I just vibe-code this? You might be surprised how far a little bit of scripting can take you.