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.jsxShell for all experiment tool pages
─ PageLayout.jsxShell for all standard pages (blog, experiments, contact…)
─ Button.jsxvariant prop: primary / secondary / link
─ Cursor.jsxCustom magnetic cursor
─ Footer.jsx
─ MemojiHead.jsxThree.js memoji β€” cursor tracking, scale-in, scroll shrink
─ Nav.jsxSticky nav with theme toggle
─ NewsletterSection.jsxMailerLite embed, styled to match DS
─ Section.jsxLayout wrapper with size prop
─ SpeechBubble.jsxTypewriter bubble tied to the memoji head
─ ThemeToggle.jsxLight / dark switcher
─ data/Single source of truth
─ experiments.jsAll experiments, newest β†’ oldest. published flag gates visibility.
─ posts.jsAll blog posts, newest β†’ oldest. published flag gates visibility.
─ blog/Blog section
─ page.jsxListing page, reads from data/posts.js
─ [slug]/One folder per post
─ page.jsxFull post with interactive demos
─ contact/Contact page
─ page.jsxCentered layout via PageLayout center prop
─ 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
─ not-found.jsx404 page with Three.js memoji and parallax digits
─ globals.cssDesign tokens + Tailwind v4 theme
─ layout.jsxRoot layout: fonts via next/font, metadata, analytics
─ page.jsxHomepage: assembles all sections, scroll-driven head animation

// 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 page uses a layout component that owns Cursor, Nav, and Footer. PageLayout wraps all standard pages (blog, experiments, contact) with optional newsletter and center props. ExperimentLayout wraps interactive tool pages with a title/description header. Changing the template means editing one file, not many.

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.

PageLayout
ExperimentLayout
Features

Self-contained, stateful components with their own logic.

MemojiHead
SpeechBubble
NewsletterSection
ThemeToggle

newsletter

Stay in the loop

New experiments, articles, and tools β€” straight to your inbox. No spam, unsubscribe anytime.