// architecture
A walkthrough of the decisions behind this codebase: folder structure, component patterns, data layer, and the rules that keep it clean as it grows.
// folder structure
The project follows Next.js App Router conventions strictly. Routes are folders, shared code lives in components/, and data files are their own layer. New contributors find what they need without asking.
// principles in practice
These are not aspirational guidelines posted in a wiki nobody reads. They are constraints enforced by the structure itself.
Experiments and blog posts are defined once in app/data/ as plain JS arrays, ordered newest to oldest. The homepage, listing pages, and any future feed all read from the same file. Adding a new post means editing one object in one place.
Every experiment page is wrapped in ExperimentLayout, a single component that owns the Cursor, Nav, Section header, NewsletterSection, and Footer. Changing the experiment template means editing one file, not five.
UI is built from a small set of primitives: Section handles all layout width and padding concerns, Button manages all variant logic, Tag and Label standardise typographic roles. Pages compose these primitives; they don't reinvent them.
Routes live where you expect them. Data lives in data/. Components live in components/. There are no flat dumps of files, no ambiguous names, no mystery folders. The structure is a map of the product.
Every colour, spacing value, and animation is a CSS custom property defined in globals.css. Light mode overrides are scoped to [data-theme="light"]. Nothing is hardcoded in a component that belongs in a token.
The site exports as a fully static build with no server required. Pagination-style features use static JSON route handlers so new patterns never force an architecture change. The site can grow from 2 posts to 200 without touching a layout.
// data flow
A post or experiment is added as a single object in app/data/. Slug, title, date, tags, description: all in one place.
The homepage slices the first 3 experiments. Listing pages render all. Both read the same file, no sync required.
Each post/experiment page is its own route. The data file holds metadata; the page file holds the content and logic.
// component model
Stateless, generic, reused everywhere. No business logic.
Compose primitives into page shells. One layout per page family.
Self-contained, stateful components with their own logic.