↩ back to the notebook

Content is a contract

Most static site generators blur the line between what you wrote and how it's shown. A theme reaches into your posts; a post hard-codes a CSS class; a redesign quietly breaks three years of archives. This project draws a hard line instead.

The contract is a type

core/content.ts reads your Markdown and produces plain, typed objects:

export type PostMeta = {
  title: string
  description?: string
  pubDate: string   // ISO date
  tags: string[]
  draft: boolean
}

export type Post = {
  meta: PostMeta
  slug: string
  html: string        // rendered body
  readingTime: number // minutes
}

That's the entire interface between your writing and everything that renders it. The presentation layer imports these as types only — it never parses Markdown, and the engine never emits site HTML. The dependency arrow points one way:

content/  →  core/ (engine)  →  site/ (presentation)  →  public/

What the contract buys you

What stays fixed

Adding posts is encouraged; reshaping them is a breaking change to every theme. So the rule is simple: change content/ to say new things, change site/ to show them differently, and leave core/ — the contract — alone unless you mean to renegotiate it.

Next: presentation is the exact opposite. It's meant to be rewritten — ideally by an agent. See Customize with an agent.