Post 001
Why This Blog Ships as Static HTML
The runtime contract behind a personal publication with no application server, database, or client framework.
The repository already contained an existing static site and a cached Cloudflare project. It did not have Git history, a content model, or room for a second deployment. The first architectural requirement was therefore more concrete than “build a blog”: add a second site without entangling the first one.
I chose a separate Astro workspace that compiles Markdown into static HTML. The company page can keep its current deployment while this site gets its own build, preview, and domain.
That sounds like an infrastructure choice. It is also an editorial choice.
The artifact is the product
Most pages here do not need to execute code in your browser. An article has a title, dates, prose, links, figures, and navigation. Those can arrive as complete HTML.
The production artifact is a directory:
dist/
├── index.html
├── about/index.html
├── writing/index.html
├── notes/<slug>/index.html
├── rss.xml
└── sitemap-index.xml
There is no application process waiting behind a request. Cloudflare serves the artifact; it does not have to render the article, query a content database, or reconstruct the page for each reader.
This keeps the operating model small:
- Markdown is the durable source.
- Git holds the history and backup.
- The build validates the publication contract.
- A static host distributes the result.
- JavaScript is reserved for an explanation that truly needs interaction.
Astro describes this model as content-driven and server-first, with components rendered to HTML and no client runtime by default.1
Figure
The blog is a compiler pipeline
- 01 Note
Portable Markdown holds the argument and its metadata.
- 02 Contract
The content schema rejects a note that cannot be published honestly.
- 03 Build
Astro resolves routes, renders HTML, and creates discovery files.
- 04 Artifact
A directory of static files becomes the deployable release.
A static site still has a runtime contract
“Static” does not mean unvalidated. It moves failures earlier.
Every note is loaded through one schema:
const noteSchema = z.object({
title: z.string().trim().min(8).max(100),
description: z.string().trim().min(24).max(220),
publishedAt: z.coerce.date(),
draft: z.boolean().default(false),
tags: z.array(tagSchema).max(8).default([]),
});
A missing description is not a page with a blank sharing card. It is a failed build. An invalid note filename is not an accidental URL. It is rejected before a release exists. Drafts render during local work and are absent from the production collection.
The contract extends past frontmatter. The standard build performs four jobs in order:
type and content checks
→ focused tests
→ static generation
→ generated-link and artifact verification
This is the publication equivalent of compiling software with a strict interface. A post is allowed to be unfinished as a draft. The release is not allowed to be ambiguous about whether it is valid.
The JavaScript budget follows the argument
There is no universal “zero JavaScript” rule. There is a reason-for-JavaScript rule.
The page you are reading contains a static system trace. HTML and CSS communicate its sequence, so adding a client framework would not help you understand it. Another note includes a contrast laboratory: changing the foreground and background lets the reader inspect the relationship directly. That interaction earns a small script on that article alone.
This creates a useful test:
If removing the script does not make the explanation materially worse, the script should not ship.
The same test applies to animation. Change, causality, and continuity can justify motion. Atmosphere cannot justify making a reader process more movement.
What this architecture refuses
The first release has no database, accounts, comments, hosted search, or content-management service. That is not a claim that those systems are bad. It is a refusal to operate them before a real editorial need exists.
The costs are not only financial. Every service adds another place where publication can fail, another data-handling decision, and another migration path. Markdown plus Git is intentionally boring: the content can survive Astro, Cloudflare, or this exact site design.
The trade-off is that publishing assumes comfort with a repository. That fits the current author. If it stops fitting, a visual editor can be evaluated against an existing portable content model instead of becoming the content model itself.
The boundary I want to keep
The site should be memorable because an explanation was unusually clear—not because the reading surface behaved like an app.
Static HTML makes that boundary easier to defend. The default is a document. Complexity has to arrive with evidence that it helps the reader.
Footnotes
-
Astro, “Why Astro?” and Astro components. Both describe build-time HTML rendering and the default absence of client-side JavaScript. ↩