Making common changes
Step by step recipes for the changes this theme needs most often.
Making common changes
Each recipe lists what changes together. Missing one of the pieces is the usual cause of a change that works locally and does nothing in production.
Add behaviour to an existing section
- Create the entrypoint in
src/entrypoints/, or beside its siblings if the section belongs to a subsystem such ashomepage-redesign/components/.
// src/entrypoints/homepage-redesign/components/my-feature.ts
import { createLogger } from '@/lib/logger';
const log = createLogger('my-feature');
document.addEventListener('DOMContentLoaded', () => {
const root = document.querySelector<HTMLElement>('[data-my-feature]');
if (!root) return;
log.info('mounted');
});- Render it from the section, at the top of the Liquid file:
{%- render 'vite-tag' with 'homepage-redesign/components/my-feature.ts' -%}-
Add a CSS entrypoint beside it if the feature needs styles, and render that too.
-
Run
npm run build. Confirmsnippets/vite-tag.liquidnow contains a branch for your path. -
Commit the source, the built assets and the regenerated snippet together.
Use createLogger rather than console.log. Console output is suppressed for customers, and you can switch a
namespaced logger on with ?ba_debug=my-feature.
Add a theme setting
Decide first whether the value belongs in theme settings at all. A value that describes a product belongs on the product as a metafield. A value that describes how a page looks belongs in theme settings or on the section.
For a global setting:
- Add it to the right group in
config/settings_schema.json, or create a group. - Read it in Liquid as
settings.your_setting_id. - If JavaScript needs it, write it to
window.themefrom a snippet and declare it insrc/@types/global.ts. - Document it in Theme settings and in the relevant Operations page.
Do not edit config/settings_data.json by hand. Shopify writes that file, and the next theme editor save or theme pull
overwrites your change.
For a section-level setting, add it to that section's {% schema %} block instead. The value then lives in the JSON
template, and each instance of the section gets its own.
Add a section
- Create
sections/my-section.liquid. - Render any entrypoints it needs at the top.
- Add a
{% schema %}block with aname,settings, and apresetsentry so it appears in the theme editor's Add section list. - Constrain it with
enabled_onordisabled_onin the schema if it only belongs on some templates. - Build, then add it to a page in the theme editor to confirm the preset works.
A section with no presets entry cannot be added through the theme editor. It can still be referenced directly from a
JSON template, which is how several of the layout-level sections here work.
Add a mini-cart element
The drawer has a working template for this at src/entrypoints/mini-cart/elements/00-example.ts. Copy it.
The contract every element follows:
- Extend
HTMLElement. - In
connectedCallback, find the parent withthis.closest('mini-cart')and throw if it is missing. - Call
parentMiniCart.execOnReady(...)and take the bus fromparentMiniCart.bus. Do not create your ownCartEventBus, because a second bus listens to the same window events but the root element will not know about your element. - Listen to
CartEvents.DebouncedChangerather thanChangedif rendering is expensive. - In
disconnectedCallback, abort the controller and unbind. - Guard the registration with
if (!customElements.get('my-element')).
Then add a matching block to sections/mini-cart.liquid so a merchandiser can place it, and a snippet that renders the
markup.
Before writing any cart mutation, read Mini-cart, in particular the serialisation rules.
Calling a SimpleCartService method from inside runExclusive deadlocks the cart.
Change something on a MediSpa page
MediSpa pages use their own layouts, their own sections and a stricter script blocklist.
- Confirm which layout the template uses. Check the
layoutkey in the JSON template. - Check the layout's
YETT_BLACKLISTbefore adding anything that depends on a third-party script. - Prefer the
medispa-*sections over the general ones. They exist because the general ones pull in jQuery or a blocked app. - Test on a real MediSpa template rather than on the homepage.
Add a value to window.theme
- Write it from a Liquid snippet:
<script>
window.theme = window.theme || {};
window.theme.myFeature = {{ my_value | json }};
</script>-
Declare it in
src/@types/global.tsunder thethemeinterface. Without this,npm run typecheckfails and the value is untyped at every call site. -
Read it in TypeScript. Treat it as possibly absent, because a page that does not render the snippet will not have it.
Change a price or a currency amount
Money in this codebase is in cents wherever it comes from Shopify, and in dollars wherever it comes from a theme setting or a metafield.
Convert exactly once, at the boundary where you format for display. src/entrypoints/utils.ts provides formatMoney,
shopifyFormatCurrency and formatCentsInMoneyTemplate for this.
The MediSpa calendar config snippet documents its own convention in a comment. Read it before touching prices there. A second conversion produces a value a hundred times too large, and reviewers tend to miss it because the code looks reasonable.
Add a translation string
- Add the key to
locales/en.default.json. - Use it in Liquid with
{{ 'your.key' | t }}. - Leave the other locale files alone unless you have translations. Shopify falls back to the default.
Schema labels go in locales/en.default.schema.json rather than the main file.
What to check before opening a pull request
npm run typecheck
npm run lint
npm run buildThen confirm:
The diff includes the regenerated assets/ files and snippets/vite-tag.liquid.
snippets/vite-tag.liquid points at hashed filenames, not at localhost:5173.
config/settings_data.json and templates/*.json contain only changes you meant to make. The dev server syncs theme
editor changes down, so these files pick up merchandiser edits.
You have merged production into your branch, so you are not about to overwrite a theme editor change. See
Branches and theme sync.