The public site. One Next.js application serving every page, every house, the checkout and these docs.
The shape
app/ one folder per route, file-system routed
docs/ this library. registry-driven, statically generated
shop/ marketplace, houses, categories, product pages
api/ server routes. forms, webhooks, small integrations
components/ shared UI. server by default
lib/ the registries. brands, docs, palette, membership, feesThe pattern that matters is lib/. Anything enumerable lives in a typed registry there and every surface reads it. The houses, the docs tree, the colour wheels, the membership ladder. A page never hardcodes a list, which is why a count can never go stale in copy.
Rendering
- Server components by default. Client components are opt-in and exist only where there is real interactivity: the cart, the docs filter, the tier switcher.
- Static where the data allows it. The docs tree is statically generated from the registry at build time, one file per section.
- Dynamic where it does not. Cart and account are per-request. Product data is fetched and cached.
- No client-side data library on the public pages. If a page can be rendered on the server, it is.
Images
Every product image is a real element with explicit dimensions, cropped to a fixed 5:6 portrait box with the subject covered rather than letterboxed. Uniform framing is what lets a grid of sellers who have never met each other look like one shop.
Styling
Plain CSS with custom properties. No utility framework and no CSS-in-JS. The design system is a token layer plus a small set of primitives, and the stylesheet is the source of truth. Where the written spec and the CSS disagree, the CSS wins and the spec gets corrected.
The colour tokens are generated from the brand system, not hand-written, and a check fails the build if a mirror drifts. See Colour.
SEO, as a build gate
Several artifacts enumerate pages by hand and cannot discover a route change on their own, so adding a page is not finished until they know about it:
- The sitemap is registry-backed where it can be. New route shapes still need adding.
- Every page exports its own title, description and canonical. A canonical is never set in a layout, because metadata inherits and it would declare every page a duplicate of one URL.
- Removing or renaming a route updates the sitemap in the same commit. A sitemap entry that 404s is a crawl error we advertised ourselves.
- Verification is done by reading the rendered HTML, not the build log. A green build proves nothing about a canonical.
Performance
- Motion stays on compositor-friendly properties. Transform and opacity, not width and top.
- Fonts are subset and preloaded only where they are critical.
- Heavy libraries are dynamically imported at the point of use, never at the root.
- Images carry explicit dimensions so nothing shifts as they load.
Deployment
Deploys are manual, on purpose. Building on push runs out of memory on this project, so a release is an explicit command from the storefront directory. It is a known rough edge rather than a preference, and it is on the list.