iii.dev website
One Next.js 16 app (App Router, static export) on Tailwind CSS v4 and
shadcn/ui over Base UI (base-nova style) that builds all of
iii.dev into dist/: prerendered HTML, nothing rendered at request time. Deployed to S3 + CloudFront by
.github/workflows/deploy-website.yml; Vercel builds previews from the same pnpm build.
| URL | Source |
|---|---|
/ |
app/(site)/page.tsx and components/landing/ (copy in components/landing/content.ts) |
/manifesto |
app/(site)/manifesto/ and components/manifesto/ |
/privacy-policy |
app/(site)/privacy-policy/ (copy in privacy-data.ts) |
/blog/, /blog/<slug>/, /blog/rss.xml, /blog/<slug>.md, /blog/index.md |
markdown posts in content/blog/; the .md twins are written by scripts/generate-blog-md.ts |
/roadmap/, /roadmap/<slug>/, /roadmap/<slug>/<file>.md, /roadmap/index.json |
tech specs read from ../tech-specs/ (markdown only, frontmatter in each README.md) |
/roadmap/<slug>/deck/ |
the interactive presentation for a spec, from roadmap/<slug>/src/App.tsx (roadmap/README.md) |
/a |
the retired A/B test route; refreshes to / |
/sitemap.xml, /llms.txt, /AGENTS.md |
generated into dist/ by scripts/ after the Next build |
/robots.txt, /favicon.svg, /og-image.png, /posthog-consent.js, /blog/<slug>/*, /console-demo/*, /fonts/* |
public/ (copied verbatim) |
app/(site) and app/(deck) are two root layouts: the site (fonts, analytics, cookie banner) and the decks (their
own Tailwind theme, roadmap/src/index.css). app/not-found.tsx carries its own document for the export's
404.html.
Local development
pnpm install # repo root (workspace)
pnpm dev # http://localhost:3100 (regenerates the roadmap manifest first)
pnpm build # the full site → dist/ (roadmap checks → manifest → next build →
# dist shaping → blog .md → llms/AGENTS → sitemap)
pnpm preview # serve dist/ at :4321
pnpm test # script unit tests + CloudFront function tests (no build needed)
pnpm test:dist # post-build contract for the whole dist/ tree (run after pnpm build)
pnpm type-check # tsc over the site and the roadmap decks
pnpm lint # biome
From the repo root: pnpm --filter iii-website <script>, or pnpm dev:website.
How the export is shaped
Next writes every page as <route>.html. scripts/finalize-dist.ts then moves the pages under blog/ and
roadmap/ into <route>/index.html, because the CloudFront edge function
(infra/terraform/website/cloudfront_functions/redirects.js) serves those two prefixes as directory sites with
trailing-slash canonical URLs. Top-level pages stay manifesto.html, privacy-policy.html: the deploy syncs the
/<page> → /<page>.html map into a CloudFront KeyValueStore from scripts/routes-kvs.ts. Adding a page is a
content-only change. Do not change these shapes; they are the site's canonical URLs.
scripts/generate-llms-agents.ts reads the homepage copy from the built dist/index.html: the hero <h1> and its
paragraph, each section's head (eyebrow, <h2>, lede), the FAQ from the page's FAQPage JSON-LD, and the footer.
Keep those shapes when editing the landing; pnpm test checks the extractor against the built page.
Writing a blog post
Add a markdown file at content/blog/<slug>.md; the filename is the URL (/blog/<slug>/). Frontmatter:
---
title: 'The Harness Is the Backend'
description: 'One or two sentences; becomes the meta description and RSS blurb.'
pubDate: 2026-05-07
updatedDate: 2026-05-09 # optional
ogImage: ../../assets/blog/<slug>/banner.png # optional; the file lives in public/blog/<slug>/
tags: [agents, architecture]
draft: false
---
Images: put the files in public/blog/<slug>/ and reference them as ../../assets/blog/<slug>/<file> in the
markdown (the historical path, rewritten at render time and in the .md export).
Adding a tech spec or a deck
See roadmap/README.md. A spec is a tech-specs/<slug>/ folder of markdown with frontmatter;
it gets a page at /roadmap/<slug>/ automatically. A deck is roadmap/<slug>/src/App.tsx; when present the spec
page links to /roadmap/<slug>/deck/.
Design system
Dark only. Geist Pixel for page and section headings (components/site/pixel-heading.tsx), Inter for every label and
sentence, Geist Mono only for code, commands and ids, all through next/font and geist. Shiki with Vesper and
Vitesse Light for code (lib/highlight.ts; markdown uses the same pair). Tokens are the shadcn variables plus
--line, --hero-accent and the --motion-* scale in app/(site)/globals.css; the JS twin of the motion scale is
lib/motion.ts. Long-form pages (blog, specs, privacy) share components/article/ and the .prose styles.
Add a shadcn component with pnpm dlx shadcn@latest add <component> from this directory; it lands in
components/ui/.
Environment
See .env.example. Production builds load GTM, PostHog and Common Room (after cookie consent) and
post email signups to Mailmodo by default. Set NEXT_PUBLIC_ENABLE_ANALYTICS=false and
NEXT_PUBLIC_MAILMODO_FORM_URL= on preview or demo projects to keep their traffic and test signups out. Live GitHub
and Discord counts need no keys; they are read at build time from public endpoints.
Deploy
Production: merge to main; deploy-website.yml builds and syncs dist/ to S3, updates the KeyValueStore route map,
and invalidates CloudFront. Vercel previews: Root Directory website, framework Next.js (set in vercel.json),
"Include source files outside of the Root Directory" enabled so ../tech-specs is available at build time.