This document gives new teammates a map of the theme with a focus on the custom services we ship (stringing, knotting, gripping, remix, and T-shirt printing). Keep it close when you are onboarding a new store or chasing a regression in one of the services.
- Install Node 18+ and pnpm. Log in with
shopify loginbefore running any CLI commands. - Pull the latest theme files with
pnpm run pull(wrap it with the correctSHOPIFY_FLAG_STORE=<store>.myshopify.com). - Start local development via
pnpm devornpm run dev. Both proxy toshopify theme devand honourSHOPIFY_FLAG_STORE. - Run
npx shopify theme checkbefore you ship to keep Liquid lint clean. - Format with
npx prettier --write .(the repo has@shopify/prettier-plugin-liquidconfigured). - Preact bundles are built in
reax/. Runcd reax && pnpm install && pnpm buildafter you touch anything underreax/src/**. - Preact entry filenames stay stable (
vx-tshirt-printing.js, etc.); shared dependencies use content-hashed filenames (vx-hooks.module-<hash>.js). Rebuild all entries together and include every emitted dependency in the theme release. - The frozen
assets/vx-MultiStepForm.jscomes from46f50aeb^and must retain exportsM,f, andg. Cached Stringing/Gripping entries from that revision still need the helper exports. Checking againstHEADalone misses these older callers; verify both pre-46f50aeband current entry graphs against retained assets. New implementations belong only in hashed chunks. - Deploy new hashed dependencies before updating entry bundles. Preserve earlier hashed assets and the legacy unversioned chunks: cached pages still import those exact URLs. Do not overwrite legacy chunks with a newer build or delete them during asset cleanup.
emptyOutDir: falsepreserves them locally; deployment must retain them too. A hard refresh is not a shopper-facing cache migration strategy.
- Theme assets live where Dawn expects them: global layouts in
layout/, sections insections/, reusable snippets insnippets/, translations inlocales/, and settings inconfig/. - Service UI is server-rendered by snippets in
snippets/and hydrated by the matching Preact bundles compiled intoassets/vx-*.jsfromreax/src/components/**. - All services write configuration onto
window.s3_*.assets/product-form.jsreads those flags when a shopper clicks "Add to cart" and adds the right mix of main product, service variants, and accessory SKUs in one bundle. - Shared styles for service cards live in
assets/services.css. Tweak in there rather than touching base Dawn CSS.
What shoppers see
Customers pick "Pro Stringing" on eligible rackets, choose a string, colour, tension, and optionally upgrade to four-knot stringing. The UI is rendered by snippets/stringing.liquid and hydrated by reax/src/components/stringing (compiled to assets/vx-stringing.js).
Toggle switches
settings.is_stringing_enabled_globallymust be true.- The product needs metafield
custom_but_hidden.studio_pro_stringing(boolean or enum). Valuestrungmakes the "factory strung" copy render. - Set
settings.stringing_collection_idto the numeric Shopify collection id that holds string SKUs (the code wraps it ingid://shopify/Collection/<id>before querying). Optional:settings.tennis_stringing_collection_idif you mirror the feature for tennis. - Enable
settings.is_knotting_enabled_globallyto surface the 2-knot vs 4-knot choice.
Shopify data to create
- Hidden service products:
- Handle
studio-pro-stringing(contains the paid service variant). - Handle
studio-four-knots-service(upsell when a customer selects four knots).
- Handle
- Collection of string products linked to
settings.stringing_collection_id. - Each string product needs:
Metafields > custom > durability | control | repulsion_power | hitting_sound | shock_absorptionwith numeric values (out of 10) used for filtering and charts.Metafield custom_but_hidden.short_description(displayed under the title).Metafield custom_but_hidden.product_tag(optional badge).- Option 1 colour values with swatches enabled so the UI pulls the hex from
optionValues[].swatch.color.
- On the racket itself, set
Metafield custom.maximum_racket_tensionso the tension picker hides options above the safe limit.
How it adds to cart
-
snippets/stringing.liquidexposes the service variant ids viawindow.s3_stringing_service_variant_idandwindow.s3_four_knots_service_variant_id. -
When the shopper submits the form,
assets/product-form.jslooks for checked inputs namedstring-variant,string-tension, andknot-configand adds:- the stringing service variant,
- the selected string variant,
- and (if four knots selected) the four-knot service variant.
The racket is added first, and each service or add-on uses Shopify's native
parent_idrelationship so the cart, checkout, and order keep the customisation grouped.
New store checklist
- Create the two hidden service products with the exact handles listed above.
- Ensure every racket that should offer stringing has the
studio_pro_stringingmetafield populated and amaximum_racket_tensionvalue. - Populate the string collection and map its numeric id into theme settings.
- Flip on the stringing and knotting checkboxes in theme settings once products, collections, and metafields are in place.
- Translate copy under
locales/en.default.json > studio.stringing.*(duplicate keys were intentional; keep them synced if you localise).
What shoppers see
Eligible rackets surface a "Gripping Service" card. Customers can keep the factory grip or pick from curated aftermarket grips. Hydration comes from reax/src/components/gripping compiled to assets/vx-gripping.js.
Toggle switches
settings.is_gripping_enabled_globallymust be true.- Product metafield
custom_but_hidden.studio_grip_servicemust be set. settings.gripping_collection_idpoints to the collection of grip SKUs.- Optional
settings.factory_grip_imageadds the marketing image for the "no grip" option.
Shopify data to create
- Hidden service product with handle
studio-gripping-service. - Collection of grip products referenced by
settings.gripping_collection_id. - Each grip product uses:
custom_but_hidden.short_descriptionfor the UI blurb.- Colour swatches on option values so the picker can render circles.
How it adds to cart
snippets/gripping.liquidemitswindow.s3_gripping_service_variant_idand a hidden input#selected-grip-variant-idthat keeps the chosen variant id and sku.assets/product-form.jschecks that hidden field and adds both the service variant and the selected grip variant as child lines of the racket.
New store checklist
- Build the grip collection, note its numeric id, and drop it into theme settings.
- Create the
studio-gripping-serviceproduct and lock it from storefront visibility. - Confirm product metafields and translations
studio.gripping.*exist.
What shoppers see
Racket, paddle, or bag products with the Remix flag render a "Personalise" line item. Clicking loads a Radix modal powered by reax/src/components/remix (assets/vx-remix.js) that overlays a 3D model with editable decals.
Toggle switches
settings.is_remix_enabled_globallymust be true.- Product metafield
custom_but_hidden.stickersmust be truthy. - The selected variant needs metafield
custom_but_hidden.rmx_3d_modelpointing to a media item or metaobject whosenamematches an assetassets/rmx-<name>.glb.
Shopify data to create
- Hidden service products:
- Handle
remixfor rackets. - Handle
paddle-remixfor pickleball paddles. - Handle
bag-remixfor products whose Shopify product type is exactlyBadminton Kitbags.
- Handle
- Variant-level metafields used to skin the configurator:
rmx_racket_frame_colorrmx_racket_grip_colorrmx_logo_colorrmx_sticker_text_color
- Optional: upload matching
mono3.ttfandSpaceMono-Regular.ttf(already in assets) if you replicate on a new store.
How it adds to cart
sections/main-product.liquidresolves the service product by product type, andsnippets/remix.liquidwrites its first variant id towindow.s3_remix_service_variant_id.- The Preact app exposes
window.s3_remix_modal_controller.openModal()so the Liquid label can open the modal. assets/product-form.jslooks for the rendered#the-stickerelement and adds the correct remix service variant with encoded sticker text.
New store checklist
- Upload the
.glbmodels toassets/with thermx-<name>.glbnaming scheme. - Populate the remix service products and hide them from the storefront.
- Configure variant metafields for colours and set the
stickersmetafield on eligible products. - Translate copy under
locales/en.default.json > studio.remix.*if you localise.
What shoppers see
Apparel flagged for personalisation shows a "Make this T-Shirt yours" option. The modal (from reax/src/components/tshirt-printing, compiled to assets/vx-tshirt-printing.js) lets customers preview up to 12 letters, periods, or spaces on a render of the product colourway. The feature-flagged upgrade adds a country and included brand logo. Its selector defaults to India and also offers Indonesia, China, Japan, Malaysia, and Denmark. Printed country names stay uppercase English while selector labels are translated.
Toggle switches
settings.is_tshirt_printing_enabled_globallymust be true.- Product metafield
custom_but_hidden.customisemust be truthy. - The optional second line requires the
theme_feature_flagmetaobject entry with handletshirt-printing-second-lineand itsenabledboolean set to true. A missing flag or missing plus service variant keeps the original one-line experience.
Shopify data to create
- Hidden service product handle
t-shirt-printing. - Unlisted two-line service product with handle
t-shirt-printing-plusand variant SKUT-SHIRT-PRINTING-PLUS(SKU matching is case-insensitive). - Variant metafields to drive the preview:
tpr_t_shirt_color(hex) for the garment fill.tpr_text_color(hex) for the printed text.
- Optional texture asset
tshirt-texture.pngalready lives inassets/; swap if the illustration changes. BarlowCondensed-SemiBold.ttfsupplies the enhanced preview font (license below). Both lines have equal cap heights; the SVG composition scales to the shirt and shrinks together for long text. Its dimensions are illustrative, not physical print measurements.- Supply transparent
lining-logo.svgandhndrd-logo.svgassets.window.s3_brandresolvesStudioto logo identifierliningandHundredtohndrd. The included logo appears beneath the country in the same color as the text. Unknown brands or failed logo loads block applying the upgrade, while name-only printing remains usable.
How it adds to cart
snippets/tshirt-printing.liquidsetswindow.s3_tshirt_printing_service_variant_idand loads the module lazily.- The Preact modal sets
#the-tshirt-textand, when applied,#the-tshirt-second-linewith the applied logo identifier indata-logo-id.assets/product-form.jsadds either the original service ort-shirt-printing-plus, never both. The uppercase country is sent as_tshirtSecondLine, with_tshirtLogoset toliningorhndrd; both use_textColor. Name-only items omit the country and logo properties. - The country cross removes the country/logo draft and preserves the name. Apply commits the new package; discard restores the previous package. The bottom Remove button clears all printing. Re-adding the country defaults to India.
- Complete desktop/mobile visual checks with both actual logos before rollout.
New store checklist
- Keep the
t-shirt-printingproduct active and directly resolvable by the Online Store theme. - Create the
t-shirt-printing-plusproduct with variant SKUT-SHIRT-PRINTING-PLUS(any letter case is accepted). Set its product status to Unlisted so Liquid can resolve it without surfacing it in search or recommendations. - Make the plus variant purchasable before enabling the feature: give it inventory, continue selling when out of stock, or disable inventory tracking. The theme deliberately keeps the one-line experience when this variant is unavailable.
- Create the reusable
theme_feature_flagdefinition with a display-name field and anenabledboolean, then create thetshirt-printing-second-lineentry. Its definition type must be exactlytheme_feature_flag, its boolean field key must be exactlyenabled, and the entry must be active. Keep it disabled until the product, font, and theme code are deployed. - Populate variant metafields for every colourway.
- Confirm the product carries the
customisemetafield toggle. - Update translations under
locales/en.default.json > studio.tshirt_printing.*when localising.
-
Create all hidden service products with handles:
studio-pro-stringingstudio-four-knots-servicestudio-gripping-serviceremixpaddle-remixt-shirt-printingt-shirt-printing-plus
Keep them in the "Services" product type. Products resolved through
all_productsmust remain active and directly accessible; use Shopify's Unlisted product status when they should stay out of search and recommendations. -
Build the collections referenced by theme settings and capture their numeric ids (Shopify Admin -> open the collection -> copy the number at the end of the URL).
-
Configure theme settings under "Product Customisations" (toggle checkboxes, paste collection ids, upload optional factory grip image).
-
Create and assign product metafields:
- Booleans for
studio_pro_stringing,studio_grip_service,stickers,customise. - Numbers for
maximum_racket_tension. - Variant-level colour metafields listed above.
- String product metric metafields under
customnamespace.
- Booleans for
-
Upload supporting assets (
rmx-*.glb, optional textures) intoassets/. -
Verify translations exist in
locales/en.default.jsonfor thestudio.*namespaces. Duplicated English strings are placeholders-replace them with store-specific copy or locales. -
Run through the CX journey in
shopify theme dev: select each service, add to cart, and confirmcart.jsshows the racket as the parent with its service and add-on child lines.
- Theme snippets:
snippets/stringing.liquid,snippets/gripping.liquid,snippets/remix.liquid,snippets/tshirt-printing.liquid. - Service scripts:
assets/vx-stringing.js,assets/vx-gripping.js,assets/vx-remix.js,assets/vx-tshirt-printing.js(source inreax/src/components/**). - Cart orchestration:
assets/product-form.js. - Shared styles:
assets/services.css. - Theme settings:
config/settings_schema.json("Product Customisations" group) and defaults inconfig/settings_data.json. - Translation keys:
locales/en.default.jsonunder thestudio.*namespace buckets.
- Always rebuild the Preact bundles (
cd reax && pnpm build) before you push service changes, otherwise Shopify will still serve the previousassets/vx-*.jsfiles. - Use the browser console to inspect
window.s3_*coverage if a bundle fails-missing values usually point to misconfigured handles or metafields. - If a collection id is wrong, the Preact fetch falls back to the "empty" translation and logs to console; keep DevTools open while testing.
- Theme Check will not catch missing metafields, so keep this README's checklist handy when onboarding a new store.
Welcome aboard-reach out in #dev-themes if any of these flows diverge from what you see locally.