The seams
A forkās src/ tree looks like one undifferentiated pile of components, and it is
not. Some of it is styling that exists to be overwritten. Some of it is a documented
seam meant to be extended. Some of it is the specific place an
invariant is enforced, and breaks without a compile error when
someone edits it as if it were the first kind. This page is the map.
1. Yours to replace
Section titled ā1. Yours to replaceāPresentation. Rewrite, restyle or delete; nothing depends on the internals, only on the data they are handed.
src/pages/LandingPage.tsxreadsstore.hero(every field optional, with a fallback to the store name and a Shop everything button) andgetFeaturedProducts/getVisibleCollectionsfromshared/catalog.ts. The collection tiles are inline here; there is no separate component to swap.src/components/layout/Banner.tsxandFooter.tsx. Both build their links fromstore.pagesand the visible collections. The footer deliberately lists every published page whether or not it is in the header menu; keep that.src/components/product/ProductCard.tsxtakes a name, a preformatted price, an image and a sold-out flag, and nothing fromshared/, so the theme editor can render one with no catalogue behind it.ProductList.tsxis the thin layer above it. If you replace the list too, keep it deriving price and sold-out state from the catalogue rather than caching them.src/index.css. The--beluga-*custom properties are the baseline every component is styled against and what Settings ā Look overwrites at runtime. Read colours and radius from these tokens in anything new.emails/*.hbs. Handlebars locals are pre-formatted display strings, so a template does no arithmetic.
2. Extend through the seam
Section titled ā2. Extend through the seamāEach of these is a function or interface with an existing example of exactly the substitution it exists for.
- The catalogueās source:
loadStoreinsrc/lib/store-source.ts. Fetches/api/storeand validates it withstoreSchema; withVITE_BELUGA_API=falseit validates the demo fixture instead. Point it at a different backend by keeping the return type, a schema-validatedPromise<Store>, the same. - Search and sort:
searchProducts/sortProductsinshared/catalog.ts, client-side against the snapshot. PastSTORE_SNAPSHOT_LIMIT(200 products), the swap is toGET /api/products?search=, which already exists, behind the samestore-source.tsfile. - Which email goes out when:
templateForStatusinserver/email.tsmaps an order status to a template. Add a status and a matching.hbspair. - Where uploaded images live:
ImageStoreinserver/image-store.ts, withput,deleteandpublicUrl. Two drivers ship, local disk and S3-compatible, chosen by environment. A third backend implements the interface; the database stores the same relative path under all of them and the storefront keeps asking for/assets/<path>.
3. Not without reading the invariants
Section titled ā3. Not without reading the invariantsāThese look like the files in section 1 and are not.
src/pages/CartPage.tsx. The cart holds identifiers and quantities, never a price or an image URL; every displayed price is re-derived from the current catalogue on render. If the cart cached a price, that is the value a stale tab would send back.src/pages/ConfirmPage.tsxpollsGET /api/checkout/:sessionIdrather than assuming payment succeeded because the buyer landed there.src/components/product/ProductDetails.tsxclamps quantity throughnormalizeQuantityon every change, including a re-clamp when switching variants, so a variant with less stock cannot keep a quantity that oversells it.server/routes/checkout.tsreads price, in cents, from the database for every id the client sent. Skip that read for a āquickā optimisation and a client can name its own price.server/routes/webhook.tsis the only placepaid,refundedand cancellation states are written, and it deduplicates by event id. Add a new webhook handler here, not a new payment-state write anywhere else.- Anything under
db/: both dialect schemas edited together, a migration for each, anddb/dialect.test.tsgreen on both engines.
4. Theming versus forking
Section titled ā4. Theming versus forkingāThe theme editor changes tokens. Forking changes components. A forked component that hardcodes a colour renders correctly until a merchant opens the theme editor, then quietly stops matching the rest of the store. Theming has the token list; Restyle the storefront walks through doing both.