I run a speed test on the SiteLens score page before I ship any landing. The current numbers: LCP under 800ms on 3G, TBT of zero, CLS of zero, Lighthouse score in the mid-90s. Every vertical. Every time.

This didn't happen by accident. It's a deliberate choice about what we include and what we don't. Let me walk through the decisions.

The Constraints

Before I built the landing-generator, I set hard constraints:

Every one of these except web fonts is non-negotiable. Web fonts are negotiable because they're a user-facing design choice Jess and I both want to keep.

The Generator

Each landing is generated from a spec.json file in scripts/specs/. The spec has the vertical's title, tagline, hero description, feature list, pricing tiers, and related-vertical links. A Python script renders the spec through a single Jinja2 template that emits the final HTML.

# scripts/build_landing.py (simplified)
import json
from pathlib import Path
from jinja2 import Template

template = Template(Path("templates/landing.html.j2").read_text())

for spec_file in Path("scripts/specs").glob("*.json"):
    spec = json.loads(spec_file.read_text())
    output_path = Path(f"net/{spec['slug']}.html")
    output_path.write_text(template.render(**spec))

130+ verticals regenerate in under 2 seconds. Change the template, everything re-renders with the new design. No build step in the user's path — they just get the pre-rendered HTML.

Inline CSS, Inline Everything

Every landing has its full CSS inlined in a single <style> block. No external stylesheet. No <link rel="stylesheet"> request. The CSS is ~5KB after minification because we use CSS variables heavily and don't load any framework.

The argument against inline CSS is cache-ability — if you load 10 pages from the same site, an external CSS loads once and caches. Inline CSS re-downloads per page.

For our traffic pattern, that's the wrong trade. Most vertical landing visitors come from a search result, land on one page, bounce or convert. They don't visit 10 different verticals in one session. Optimizing for "single page load is as fast as possible" beats optimizing for "the 10th page loads 5KB lighter."

No JavaScript Framework

React is not on these pages. Neither is Vue, Alpine, or Svelte. The only JavaScript is ~60 lines inline: mobile hamburger menu toggle, scroll-reveal for cards as you scroll down, and a nav-background-darkening effect on scroll. All of it is vanilla DOM API.

The win isn't philosophical — it's practical. A React landing page means shipping 40-80KB of React bundle, parsing it, executing it, before any content shows. Mine has ~2KB of inline JS that runs after the page is painted. The LCP gap is enormous.

I've got nothing against React for apps. I've got something against React for landing pages that are fundamentally static content.

Images and the Hero

Most of our landings don't have a hero image. The hero is typography + layout + the Dangercorn grid background. Zero image bytes on most pages.

Pages that do have images (the occasional product screenshot) use WebP with a JPEG fallback, responsive srcset, and loading="lazy" everywhere except above-the-fold. Above-the-fold images preload. Below-the-fold lazy-load.

The Font Trade

Google Fonts is the one external resource we load. DM Sans + DM Serif Display + JetBrains Mono. With preconnect hints, the font load is parallel with the HTML parse and usually doesn't block LCP.

Self-hosting fonts would shave another ~50ms and remove an external dependency. We've decided not to because (a) Google Fonts has better global CDN coverage than our one-server hosting, (b) the fonts change rarely, and (c) maintaining font subsetting for 130+ pages is its own category of work we don't want.

The OpenGraph Image

Every landing has an OG image (the thing that shows up in Slack/Twitter/LinkedIn previews). We generate these programmatically with Playwright — render the landing page, screenshot the hero section, save as og-{slug}.png. Runs at build time. Cached.

This is more work than using a generic site-wide OG image, but it's dramatically better when the landing gets shared. A generic OG image looks lazy; a per-page one looks intentional.

What 13KB Buys You

Concretely: on a 3G connection, a 13KB HTML page transfers in under 600ms including TCP handshake. On broadband, under 100ms. LCP follows shortly after because there's no waiting for external assets or hydration.

On mobile, the improvement is larger than on desktop. Mobile browsers parse and paint smaller HTML much faster because of lower-end CPUs. A 200KB Next.js page that renders in 200ms on my desktop renders in 1,800ms on a budget Android. 13KB renders in 400ms on the same budget Android.

What You Give Up

No client-side interactivity beyond what 60 lines of vanilla JS can do. If a landing needed a calculator widget or a complex interactive demo, we'd have to add JS — and I'd reach for HTMX or Alpine, not React.

No A/B testing infrastructure in the landing itself. We can still test via server-side variants (render two different slug-a.html and slug-b.html, route randomly), but we can't do fancy in-browser experiments.

No third-party analytics on-page by default. We measure traffic from server logs. If we wanted Google Analytics or Plausible, it'd cost some bytes. We've opted out.

What This Enables

Running 130+ landings on a $5 VPS with nginx handling static files. Zero backend load per landing visit. SEO crawls complete in seconds — Googlebot sees every landing as one fast request, indexes it immediately. Sitemap generation is trivial (every file in net/ is a URL).

It also enables us to iterate on the design. Change the template, rebuild 130 pages in 2 seconds, push to git, deploy. No cache-warming, no incremental rebuilds, no framework-level restart.

The cheapest, fastest, simplest web page is a pre-rendered HTML file on an nginx-served disk. Every layer you add on top should justify itself. Most can't.

Related

The app template (different concerns, same philosophy). Deterministic ports. Flask + SQLite at scale. Examples in the wild: cheesemaking, shiftfill, meetingmind. Run a speed test on any of them.