
Building br3ak.com in public (09/05)
The store is not open yet; the site went up first, and the whole thing took five hours. Full stack, why not Shopify's own Hydrogen, why there is no database, what actually cost the time, and how to start if you want to build one yourself.
BR3AK has nothing to sell yet. No supplier selected, no material qualified.
The usual move is to wait — finish the product, then open the site and the store together. This site does the opposite: run the brand on content first, and leave the store until there is a real product.
The whole site took me five hours. This is how the architecture was decided, what is actually running, and how to start if you want to build one yourself.
One hard requirement, and most options disappear
The whole architecture falls out of a single sentence:
I can run this brand with Claude Code, without logging into an admin.
That is not a slogan. It is a hard requirement that rules out a lot of options — every decision below comes from it.
Why not Shopify's own Hydrogen
Shopify has its own frontend framework, Hydrogen, running on its own hosting platform, Oxygen. The instinct is that deep Shopify integration means using Shopify's stack.
Two things turned up.
Agent capability has nothing to do with the frontend framework. Every Shopify store already exposes MCP endpoints on its own — no API key, no app to build:
https://{shop}.myshopify.com/api/mcp— policies, FAQ, ordershttps://{shop}.myshopify.com/api/ucp/mcp— catalog lookup
Hydrogen's endpoint is a proxy of that onto your own domain, not the source. So "agent-native means Hydrogen" does not hold — Next.js reaches the same thing.
Hydrogen is incompatible with launching for free. Hydrogen requires a Shopify store, and Oxygen requires a paid plan. Taking that route means paying before a single product exists.
So the frontend is Next.js on Vercel, with the store switch off.
The stack, as it actually runs
| Layer | What |
|---|---|
| Framework | Next.js 16 (App Router) + React 19 |
| Deploy | Vercel, push to main and it ships |
| Content | MDX files in the repo, gray-matter for frontmatter, next-mdx-remote to render |
| Database | None |
| Validation | zod schema — broken frontmatter fails the build |
| Locales | [locale] routes, English default plus Traditional Chinese, each with its own URLs |
| Config | One brand.config.ts — name, domain, locales, nav, description, all in one place |
| Copy | A single bilingual file; only long-form goes into MDX |
SEO groundwork (do this on day one; retrofitting hurts): sitemap.xml, robots.txt, per-page canonical and hreflang, an RSS feed, and four kinds of JSON-LD — Organization, Brand, BlogPosting, BreadcrumbList.
Machine-readable endpoints:
/api/brand.json— name, tagline, description, locales, nav/api/products.json— product list (empty for now)/llms.txt— a plain description of the site for language models
People read the page, agents read the JSON. That layer goes in now.
No database
Everything on this site — brand story, articles, page copy — is a file in the repo. That is not laziness; it is the requirement.
Files: edit, commit, push, deploy. The whole loop happens in a terminal. A database: credentials, an API, a publish script, migrations. Every database you add is one more door that has to be opened by logging in — which is exactly what the requirement forbids.
When a store gets connected later, the same line holds: anything with live state stays on the store side. No second copy here. A second copy goes stale, and stale data is the worst bug in commerce.
The other direction too: brand story, articles, page copy stay in the repo — that is precisely what an agent should be editing directly.
The cost is no /admin UI. If a non-engineer on the team needs to edit content later, the answer is not to add a database — it is a git-based editor (Keystatic, TinaCMS, Decap): an interface for people, writing to the same files git already holds.
This site was built in a terminal
The process is that last sentence, expanded: Claude Code edits the files, runs a build, commits, pushes. Vercel takes it from there.
Swapping a hero image, changing a brand term, adding a page, publishing an article — all the same motion. No admin was logged into. No dashboard was clicked. This article was written the same way.
What actually cost the five hours
The architecture costs nothing once it is decided — you just follow it. These are what ate the clock.
No products, but a pile of commerce components left over. There was a size selector, a colourway swatch row, and an add-to-cart button. With nothing to sell they are all noise — but the worse problem came after add-to-cart was repurposed into a content CTA: the button said READ THE JOURNAL and the link pointed somewhere else entirely. A mismatch between label and destination is invisible on screen. You have to read the href to catch it.
text-transform: uppercase eats the casing in your brand terms. The design concept was written ReForm, where the capital F is doing the work — it makes you read re + form rather than reform, which means something closer to correcting a fault. But three places on the site apply uppercase, and there it renders as REFORM. The meaning quietly slides. It became RE-FORM in the end — a hyphen survives uppercasing. If your brand term carries meaning in its casing, check whether CSS is going to eat it.
Renaming a route means grepping the prose too. Changing /transparency to /contact left four markdown links in the about page and articles pointing at the old route, all of them 404s. Renaming a route is never one file.
grep is case-sensitive by default. A site-wide terminology change missed one all-caps TRANSFORMABLE, because the search string was lowercase. It got reported as clean when it was not. Re-check with grep -i.
External resources quietly become dependencies. One CSS background image pointed at a file on a third-party CDN. It works, so nobody notices — until that file is deleted or changed and your homepage breaks with it. Now served from the repo.
A cut-out image depends on where it lands. Two lookbook frames use mix-blend-mode: multiply, where a white background gets multiplied away and disappears. Put the same file in a frame without multiply and you get a white box. The asset was fine; the slot decides whether it can be used.
Building a site with Claude Code
Here is the order I would use with Claude Code, starting from nothing. The point is not to say "build me a website" — it is to set the boundaries first, then let it fill them in.
1. Start from a clean project
create-next-app with TypeScript. Do not fork something feature-heavy and delete your way down — you never get it clean, and you spend the whole time reverse-engineering somebody else's architecture.
2. Write AGENTS.md before you write code
This is the step that matters most and the one most people skip. Put an AGENTS.md in the repo root spelling out the rules: where content lives, how bilingual copy is split, what must never enter the repo, what command to run after changes.
Claude Code reads it every time it starts. Without that file you re-explain the project on every session. With it, the rules become part of the project.
3. Content layer: files plus schema validation
A content/ directory of MDX, gray-matter for frontmatter, and then a zod schema that fails the build when validation fails.
This is the safety net for agent-written content. Broken frontmatter is normal, not exceptional; with a schema the error surfaces at build time instead of shipping quietly as a broken page.
4. One config file
Brand name, domain, locales, nav, description — all in one brand.config.ts. Change the nav once and the header, footer, llms.txt and brand.json all follow.
You get paid back the first time you change the nav: if it lives in four places, you will miss one.
5. Do the SEO layer in one pass
sitemap.ts, robots.ts, canonical, hreflang, JSON-LD, feed. Every brand needs all of it, and retrofitting hurts — especially bilingual. If the second language has no URLs of its own, it does not exist to Google, and fixing that later means moving your routing.
6. Leave the store switch off
Put a commerce.provider in the config, set to 'none'. Do not touch commerce before there is a product. Get the content site running first.
7. When you do connect a store
Hold the same line: do not sync product data into the repo. Anything with live state gets asked for at render time; the repo holds one identifier and nothing more.
8. The loop
Edit, npm run build, commit, push. Let the build be the gate — it passes, then it ships.
The supply chain is not ready, and that is not hidden — it is written on the site. Content runs first. The product follows.

