Always cooking something

// architecture

How this site
is built

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

Everything has a place

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.

project structure
app/Next.js App Router root
โ”€ components/Shared UI primitives
โ”€ layouts/Page shell components
โ”€ ExperimentLayout.jsxShared shell for all experiment pages
โ”€ Button.jsxvariant prop: primary / secondary / link
โ”€ Cursor.jsxCustom magnetic cursor
โ”€ Footer.jsx
โ”€ Nav.jsxSticky nav with theme toggle
โ”€ NewsletterSection.jsxMailerLite embed, styled to match DS
โ”€ Section.jsxLayout wrapper with size prop
โ”€ ThemeToggle.jsxLight / dark switcher
โ”€ data/Single source of truth
โ”€ experiments.jsAll experiments, newest โ†’ oldest
โ”€ posts.jsAll blog posts, newest โ†’ oldest
โ”€ blog/Blog section
โ”€ page.jsxListing page, reads from data/posts.js
โ”€ [slug]/One folder per post
โ”€ page.jsxFull post with interactive demos
โ”€ experiments/Interactive tools
โ”€ page.jsxListing page, reads from data/experiments.js
โ”€ [tool]/One folder per experiment, wrapped in ExperimentLayout
โ”€ architecture/This page
โ”€ design-system/Token & component explorer
โ”€ globals.cssDesign tokens + Tailwind v4 theme
โ”€ layout.jsxRoot layout: fonts, metadata, theme
โ”€ page.jsxHomepage: assembles all sections

// principles in practice

The rules behind the structure

These are not aspirational guidelines posted in a wiki nobody reads. They are constraints enforced by the structure itself.

โ—ˆ

One source of truth

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.

DRYData Layer
โฌก

Shared layout components

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.

Component ReuseTemplates
โŠž

Composable primitives

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.

CompositionPrimitives
โŒ—

Folder mirrors mental model

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.

File StructureClarity
โ—

Tokens, not magic numbers

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.

Design TokensTheming
โ†—

Static by default, scalable by design

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.

Static ExportScalability

// data flow

How content moves through the site

01

Defined once

A post or experiment is added as a single object in app/data/. Slug, title, date, tags, description: all in one place.

02

Read everywhere

The homepage slices the first 3 experiments. Listing pages render all. Both read the same file, no sync required.

03

Page renders itself

Each post/experiment page is its own route. The data file holds metadata; the page file holds the content and logic.

data flow: blog posts
app/data/posts.js
Single array of post objects. Ordered newest โ†’ oldest. Imported by any page that needs it.
โ†“ ย  imported by
app/page.jsxslices first 3, renders cards
app/blog/page.jsxrenders full listing

// component model

Primitives, layouts, and features

Primitives

Stateless, generic, reused everywhere. No business logic.

Button
Section
Cursor
Nav
Footer
Layouts

Compose primitives into page shells. One layout per page family.

ExperimentLayout
Features

Self-contained, stateful components with their own logic.

NewsletterSection
ThemeToggle
BezierCanvas
BlobEditor