Post 001

Why This Blog Ships as Static HTML

The runtime contract behind a personal publication with no application server, database, or client framework.

By Neri

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

  1. 01 Note

    Portable Markdown holds the argument and its metadata.

  2. 02 Contract

    The content schema rejects a note that cannot be published honestly.

  3. 03 Build

    Astro resolves routes, renders HTML, and creates discovery files.

  4. 04 Artifact

    A directory of static files becomes the deployable release.

The contract is checked before deployment, so a malformed note fails as a build input rather than becoming a broken production page. Source: This site’s production build

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

  1. Astro, “Why Astro?” and Astro components. Both describe build-time HTML rendering and the default absence of client-side JavaScript.