Everything the theme can do, and how.
Written for the merchant who is setting Regimen up alone. Start at the top; the launch checklist near the end is the one to print.
Getting started
1. Upload the theme
Shopify admin → Online Store → Themes → Add theme → Upload zip file →
choose Regimen-by-Foxfora-1.1.2.zip (inside your package). Do not publish it yet.
2. Pick a preset
Customize → Theme settings → the preset picker at the top.
Regimen ships three. Each one changes colours, all three fonts, corner radius and spacing density together, so switching is a real change of character rather than a recolour.
| Preset | Reads as | Good for |
|---|---|---|
| Regimen | Warm off-white paper, ink, deep green. Serif headings. | Apothecary, herbal, traditional formulations |
| Clinic | White, near-black, electric blue. Grotesque throughout. Compact. | Clinical, evidence-led, sports nutrition |
| Ritual | Sand and terracotta, soft corners, generous spacing. | Skincare, beauty, self-care, wellness rituals |
Switching a preset overwrites your colour, font and layout settings. Pick one before you start customising.
3. Install Search & Discovery
Collection filtering and complementary product recommendations both come from Shopify's own free Search & Discovery app. Install it, then:
- Filters → add the filters you want. Availability, price and your variant options are the ones worth having first.
- Product recommendations → pair complementary products by hand. These are what show in the "Pairs well with" section and in the cart drawer.
Regimen needs no other app.
4. Set up your metafields
This is the step that unlocks Regimen's wellness features. The supplement facts panel, the certificate of analysis block, the dosage chart and the product descriptor all read product metafields.
Follow 04-metafields.md. It takes about ten minutes and there is a
copy-and-paste script that creates all nine definitions at once.
5. Menus
Content → Menus. Regimen supports three levels. To turn a top-level item into a mega menu, add a Mega menu block in the Header section and type that menu item's title into the block's "Top-level menu item" field, exactly as it appears in the menu.
6. Work through the home page
The home page arrives fully built with thirteen sections and real placeholder copy, arranged in the order that tends to convert for this category:
- Hero
- Trust icon bar
- Best sellers
- Shop by goal
- Ingredient spotlight
- Brand story
- Routine timeline
- Testing transparency
- Reviews
- Comparison table
- FAQ
- Journal
- Email signup
Replace the copy, point the collection sections at your collections, and add your images. Delete what you do not need — nothing depends on anything else.
7. Before you publish
Run through 07-launch-checklist.md.
Recommended image sizes
| Where | Size | Shape |
|---|---|---|
| Logo | 400 × 100 px or larger | Any |
| Hero / image banner | 2400 × 1350 px | 16:9 |
| Hero, mobile | 1200 × 1500 px | 4:5 |
| Slideshow slide | 2400 × 1350 px | 16:9 |
| Product photography | 2000 × 2000 px | 1:1, or keep one ratio throughout |
| Collection tile | 900 × 1200 px | 3:4 |
| Promo tile | 1400 × 1050 px | 4:3 |
| Ingredient image | 800 × 800 px | 1:1 |
| Article featured image | 2000 × 1125 px | 16:9 |
| Favicon | 96 × 96 px | 1:1 |
| Social sharing image | 1200 × 628 px | ~1.91:1 |
Regimen generates responsive sizes automatically from whatever you upload, so upload the largest clean version you have and let the theme resize it. Set a focal point on any image that crops (Shopify Content → Files).
Theme settings
Everything global lives in Customize → Theme settings. Settings are grouped by where you see the result, not by how the code is organised.
Logo and favicon
Main logo, a separate mobile logo, and a light logo used when the header is transparent over dark hero imagery. Any aspect ratio works — portrait marks are fine. Widths are set separately for desktop and mobile. The social sharing image is the fallback used when a page has no image of its own.
Colors
Six colour schemes. Each scheme is a complete set: background, optional background gradient, text, accent, solid button, solid button label, outline button, border and shadow. Every background has a paired foreground, so a badly contrasting combination is hard to produce by accident.
Schemes are applied per section, and on some sections per block, so one page can alternate light, tinted, dark and accent bands without you touching CSS.
Sale, in-stock, low-stock and out-of-stock colours sit outside the schemes because they must stay legible in all of them.
Typography
Three fonts, and the third is what gives Regimen its character:
- Heading — display type. Size, tracking, leading and case.
- Body — running text. Size, tracking, leading.
- Label — the small-caps microtype used on badges, fact panels, dosage steps, navigation and buttons. Widening its letter spacing is the single fastest way to change how clinical the store feels.
Headings use a fluid clamp() scale, so the size setting scales the whole ramp
rather than one breakpoint.
Page layout
Page width, side margin and grid gap. Spacing density (compact, default, spacious) multiplies the vertical rhythm across every section at once — the quickest global change you can make. Then three corner radius tokens (blocks, buttons, images), border width, and shadow strength and blur.
Set shadow strength to 0 for a flat, editorial look. That is the Regimen default.
Animations
Scroll reveal, stagger, and an image hover effect (none, zoom, lift). All motion is disabled automatically for visitors whose device asks for reduced motion — you do not need to think about it.
Buttons
Solid or outline primary style, label or body font, and full-width buttons on mobile.
Product cards
Image shape and fit, second image on hover, vendor, rating, colour swatches, quick add, the short descriptor line, text alignment, and card border style.
Set Image fit to "Fit whole image" for packaging shots that must never crop — a common requirement for supplement tubs and boxes.
Badges
Position, what the sale badge shows (the word, the percentage, or the amount), the sold-out badge, and how long a product counts as new. Set the new-badge window to 0 to turn it off.
Cart
Drawer or page, order notes, free-shipping progress and its threshold, complementary add-ons, and an optional terms checkbox that gates checkout.
The threshold is in your store currency without decimals or symbols: 5000 means 50.00 on a two-decimal currency.
Search
Drawer or full-screen overlay, live suggestions, prices in suggestions, whether articles and pages are included, and a comma-separated list of popular searches shown before anything is typed.
Variants and swatches
Buttons or dropdowns, swatch shape and size, and how unavailable combinations appear: struck through, faded, or hidden. Swatch colours and images come from Shopify's own product option values (Settings → Metaobjects), not from the theme.
Wellness and supplement
Defaults for the fact panel heading and footnote, the compliance disclaimer shown under the panel and in the footer, and the certificate link label. Each can be overridden per product with a metafield.
Edit the disclaimer to match the rules where you sell. Clear it if your category does not need one.
Social media
Eight platform links. Only the ones you fill in are rendered — there are no empty icons. Plus a global toggle for share buttons on products and articles.
Sections
49 sections, 33 of which you can add from the editor. They are grouped by category in the Add section picker.
Wellness — the ones only Regimen has
Ingredient spotlight. One block per active. Each carries a name, a dose, an optional secondary or latin name, why it is in the formula, where it is sourced, and a reference link. Cards or rows layout. This is the section that lets you argue your formulation rather than just claim benefits.
Routine timeline. A protocol over time — a dosing schedule, a skincare routine, or an expected-results timeline. Horizontal or vertical, with an icon or a step number per step, and a footnote field that is pre-filled with an honest "results vary" line.
Testing transparency. A heading and text beside a bordered panel of test results you type in, each with an optional tick. Plus a button to the certificate. Nothing is pre-filled with a result — you supply every value.
Pack picker. A radio group where each option is a real product. Prices, compare-at prices, per-unit prices and sold-out states all come from that product's own data, and the form adds the chosen variant to the cart directly. Create one product per pack size, then pick each one in a block.
Banners
Image banner — hero with separate mobile art, four heights, overlay darkening, nine text positions, and an optional solid panel behind the text for legibility over busy photography.
Slideshow — up to six slides, per-slide image, mobile image, overlay, text, position, alignment and two buttons. Autoplay is off by default, pausable, and never advances for anyone who asks their device to reduce motion.
Promo tiles — two, three or four across, or one large with two small. Each tile gets its own colour scheme, overlay and text position.
Products
Featured collection — grid or carousel, 2–5 columns, view-all link.
Featured product — a full buy form anywhere, with its own block list and
@app block support.
Product recommendations — related or complementary, loaded lazily so it
never costs you page speed.
Recently viewed — remembered in the visitor's own browser, nothing sent
anywhere.
Collections
Collection list — grid or carousel of collection tiles, three shapes, optional product counts.
Text
Rich text, Image with text (with a checklist and statistic block), Multicolumn (icon, image, step number or nothing above each column, three column styles), Collapsible content (FAQ, heading beside or above the rows), Blog posts, Email signup, Scrolling text.
Trust
Icon bar, Logo list (static or scrolling with a pause control), Testimonials (three card styles, grid or carousel), Comparison table (two or three columns, ticks, crosses or text), Statistics (with a source-note field).
Media
Video (Shopify-hosted, or YouTube and Vimeo loaded only on click so the embed never costs you page speed), Before and after (a keyboard-accessible slider with a required disclosure field).
Promotion
Countdown (a real deadline; it hides or freezes when it passes and never resets), Email popup (delay or scroll trigger, per-visitor frequency), Age verification (remembered for the browsing session).
Advanced
Custom section — the open canvas. It accepts any theme block, nested up to eight levels, so a layout Regimen does not ship as a named section can still be built without code.
Custom Liquid and Apps — for app snippets, embeds, and anything else.
Theme blocks
Eleven blocks work inside the Custom section and inside each other:
Group (the layout container — nest it to build rows, columns and grids), Heading, Text, Button, Image, Icon and text, Divider, Product card, Collapsible row, Fact row, Custom Liquid.
A Group set to horizontal, containing two Groups, each containing a Heading and a Text, is a two-column feature block — built entirely in the editor.
Header and footer
Both are section groups, so they are edited on any page and can hold more than one section. The header group ships with the announcement bar above the header. The footer group ships with an icon bar above the footer.
Footer blocks: Brand, Menu (repeatable), Text, Email signup, Contact details,
and @app. On mobile, menu and text blocks collapse into accordions.
Metafields
Regimen's wellness features are driven by product metafields, so every product carries its own panel, dose list, chart and certificate. Nothing is faked and nothing is global.
Every one is optional. A block whose metafield is empty renders nothing at all — it does not leave a gap or a placeholder.
The nine definitions
Create these under Settings → Custom data → Products → Add definition.
| Name | Namespace and key | Type | Drives |
|---|---|---|---|
| Short descriptor | custom.cutline |
Single line text | The green line under the product title, and the line under each card title. For example "60 capsules / 30 days". |
| Badge | custom.badge |
Single line text | An extra outlined badge on the product card and gallery. For example "New formula". |
| Supplement facts | custom.supplement_facts |
JSON | The full facts panel. Shape below. |
| Supplement facts (text) | custom.supplement_facts_text |
Rich text | A fallback panel for products where a table is overkill. Used only when the JSON field is empty. |
| Size or dosage chart | custom.size_chart |
Rich text | The chart dialog that opens beside the variant picker. Takes priority over the page chosen in the section settings. |
| Certificate of analysis | custom.lab_report |
File | The certificate link in the lab results block. Upload the PDF under Content → Files. |
| Batch | custom.lab_batch |
Single line text | The batch row in the lab results block. |
| Tested on | custom.lab_tested_on |
Date | The tested-on row. |
| Tested by | custom.lab_tested_by |
Single line text | The lab's name. |
Ratings come from reviews.rating and reviews.rating_count, which are
Shopify standard definitions that most review apps write to automatically. You
do not create these by hand. If a product has no reviews, Regimen renders no
stars rather than an empty five.
Creating all nine at once
Paste this into the Shopify GraphiQL App (install it from the Shopify App Store, it is free and made by Shopify) and run it once. It creates every definition with the right type and pins it to the product editor.
mutation CreateRegimenMetafields {
cutline: metafieldDefinitionCreate(definition: {
name: "Short descriptor", namespace: "custom", key: "cutline",
ownerType: PRODUCT, type: "single_line_text_field", pin: true,
description: "Shown under the product title and on product cards, e.g. 60 capsules / 30 days"
}) { createdDefinition { id } userErrors { field message } }
badge: metafieldDefinitionCreate(definition: {
name: "Badge", namespace: "custom", key: "badge",
ownerType: PRODUCT, type: "single_line_text_field", pin: true,
description: "An extra badge on the product card"
}) { createdDefinition { id } userErrors { field message } }
facts: metafieldDefinitionCreate(definition: {
name: "Supplement facts", namespace: "custom", key: "supplement_facts",
ownerType: PRODUCT, type: "json", pin: true,
description: "The supplement facts panel. See the theme documentation for the shape."
}) { createdDefinition { id } userErrors { field message } }
factsText: metafieldDefinitionCreate(definition: {
name: "Supplement facts (text)", namespace: "custom", key: "supplement_facts_text",
ownerType: PRODUCT, type: "rich_text_field", pin: false,
description: "Fallback panel, used only when the JSON field is empty"
}) { createdDefinition { id } userErrors { field message } }
sizeChart: metafieldDefinitionCreate(definition: {
name: "Size or dosage chart", namespace: "custom", key: "size_chart",
ownerType: PRODUCT, type: "rich_text_field", pin: false,
description: "Opens in a dialog beside the variant picker"
}) { createdDefinition { id } userErrors { field message } }
labReport: metafieldDefinitionCreate(definition: {
name: "Certificate of analysis", namespace: "custom", key: "lab_report",
ownerType: PRODUCT, type: "file_reference", pin: true,
description: "The PDF a customer can open from the lab results block"
}) { createdDefinition { id } userErrors { field message } }
labBatch: metafieldDefinitionCreate(definition: {
name: "Batch", namespace: "custom", key: "lab_batch",
ownerType: PRODUCT, type: "single_line_text_field", pin: false
}) { createdDefinition { id } userErrors { field message } }
labTestedOn: metafieldDefinitionCreate(definition: {
name: "Tested on", namespace: "custom", key: "lab_tested_on",
ownerType: PRODUCT, type: "date", pin: false
}) { createdDefinition { id } userErrors { field message } }
labTestedBy: metafieldDefinitionCreate(definition: {
name: "Tested by", namespace: "custom", key: "lab_tested_by",
ownerType: PRODUCT, type: "single_line_text_field", pin: false
}) { createdDefinition { id } userErrors { field message } }
}
If a definition already exists you will get a TAKEN error for that one field
only; the rest still get created.
The supplement facts JSON shape
Paste this into the Supplement facts field on a product and edit the
values. Every key is optional except rows.
{
"serving_size": "2 capsules",
"servings_per_container": "30",
"rows": [
{ "name": "Magnesium (as magnesium glycinate)", "amount": "300 mg", "dv": "71%" },
{ "name": "of which elemental magnesium", "amount": "300 mg", "indent": true },
{ "name": "L-theanine", "amount": "200 mg" },
{ "name": "Vitamin B6 (as P-5-P)", "amount": "1.4 mg", "dv": "100%" }
],
"other_ingredients": "Vegetable cellulose capsule, rice flour",
"allergens": "Produced in a facility that also handles milk and soy",
"note": "Daily value not established."
}
| Key | What it does |
|---|---|
serving_size |
Left side of the panel's meta row |
servings_per_container |
Right side of the meta row |
rows |
The table. Each row needs name; amount and dv are optional. |
rows[].indent |
Set true to indent the row, for a sub-component of the row above |
rows[].dv |
Percent daily value. A row with no dv shows a dagger instead. |
other_ingredients |
The footer line under the table |
allergens |
A second footer line |
note |
Replaces the dagger footnote. Falls back to the theme setting. |
A row with no dv renders †, and the footnote explains it. That is the
convention regulators and buyers both expect, and it is why the field exists.
Doing it in bulk
For more than a handful of products, use Products → select all → Bulk edit and add the metafield columns, or a CSV import via the Matrixify app. The JSON field takes a single-line JSON string in a CSV cell.
Which blocks read what
| Product page block | Reads |
|---|---|
| Short descriptor | custom.cutline |
| Rating | reviews.rating, reviews.rating_count |
| Supplement facts | custom.supplement_facts, then custom.supplement_facts_text |
| Lab results | custom.lab_report, custom.lab_batch, custom.lab_tested_on, custom.lab_tested_by |
| Variant picker (chart link) | custom.size_chart, then the section's chart page |
| Collapsible row | Any metafield you connect with the dynamic source button |
The Collapsible row block is the general-purpose one. Its "Metafield content" field has a dynamic source button, so you can point it at any rich text metafield you already have — ingredients, sourcing, clinical references, whatever you keep per product.
The product page
Blocks
Every element of the buy column is a block you can reorder, hide or remove:
Vendor · Title · Short descriptor · Rating · Price · Payment terms · Stock status · Variant picker · Quantity · Buy buttons · Pickup availability · Description · SKU · Supplement facts · Lab results · How to use · Dietary labels · Trust lines · Collapsible row (repeatable) · Share · Custom Liquid · App blocks
The order that ships is the one that tends to convert for this category: price and stock status above the variant picker, trust lines immediately under the buy button, and the fact panel below the fold where a considered buyer goes looking for it.
Gallery layouts
Four, set in the section settings:
- Thumbnails below — the default. Works at any catalogue size.
- Thumbnails beside — desktop thumbnails in a column next to the stage.
- Stacked column — every image in one scrolling column. Good for long-form, editorial product pages.
- Two column grid — first image full width, the rest two-up.
All four support video, YouTube and Vimeo embeds, and 3D models. Clicking an image opens a full-screen lightbox. Selecting a variant switches the stage to that variant's image automatically.
Images of different aspect ratios do not break the layout — each media item carries its own ratio.
Variant picker
Built from your product options, not by looping variants, so it stays fast on large catalogues and supports Shopify's native swatches.
- An option whose values have swatches renders as colour or image swatches automatically. Everything else renders as buttons, or dropdowns if you prefer.
- Combinations that do not exist are struck through, faded or hidden — your choice in theme settings.
- Selecting a variant updates the price, unit price, savings, stock status, SKU, instalments banner, quantity rules, volume pricing and the gallery, and writes the variant to the address bar so the link is shareable.
That update is done by asking Shopify to re-render the section for the chosen variant, so what you see is always what the server would have sent. It cannot drift out of sync with your real data.
Without JavaScript the picker falls back to a plain variant dropdown inside the form, and add-to-cart still works.
Quantity, rules and volume pricing
The quantity stepper respects B2B quantity rules — minimum, maximum and increment — and shows them as a note. If a variant has quantity price breaks, they render as a table under the stepper.
Sticky add-to-cart bar
Appears once the buy column scrolls out of view. Shows the product thumbnail, title and live price on desktop, and a full-width button on mobile. It submits the real product form, so the selected variant and quantity carry over.
Turn it off in the section settings.
Alternate templates
Product is the default. Product landing is a long-form template with a grid gallery, the pack picker, the routine timeline, the testing panel, reviews and FAQ — for a hero SKU you send paid traffic to.
Assign a template per product in the admin under Product → Theme template.
What the page will not do
- It will not show a stock number you did not stock. The low-stock line reads real inventory and only appears below the threshold you set.
- It will not show stars for a product with no reviews.
- It will not show a fact panel, a certificate, or a dosage chart for a product where you have not filled the metafield in.
Collections, filtering and search
Filters
Filters come from Shopify's free Search & Discovery app, not from the theme. Install it, then Search & Discovery → Filters and add what you want. Availability, price and your variant options are the ones worth having first. Shopify allows up to 25 filters.
Regimen renders whatever you add: checkbox lists with per-value result counts, a price range with numeric inputs, and colour or image swatches wherever an option value has a swatch.
Layouts. Sidebar or a horizontal bar on desktop; a drawer on mobile with apply and clear.
Behaviour. Changing a filter or the sort order updates the grid, the filter counts, the active-filter chips and the pagination in place — no page reload — and pushes the new URL so the browser back button works and the filtered view is shareable. Price inputs are debounced so typing does not fire a request per keystroke.
Active filters appear as chips above the grid, each removable on its own, with a clear-all link.
Without JavaScript the filter form submits normally and everything still works.
Pagination
Three modes: numbered pages, a load-more button, or load-as-you-scroll.
Load more is the default. Infinite scroll is available but not recommended as a
default — it breaks footer access, back-navigation and deep-linking. If you turn
it on, numbered pagination is still rendered inside a <noscript> so the
collection stays crawlable.
Product cards
Configured once in Theme settings → Product cards and used in every grid and carousel in the theme. Image shape and fit, second image on hover, vendor, rating, colour swatch row with a "+N" overflow, the short descriptor line, quick add, alignment and border style.
Quick add behaves differently by product: a single-variant product goes straight into the cart; a multi-variant product opens a quick view with the full buy form, gallery image and variant picker.
Collection page extras
Collection hero with optional image and an overlay text panel, breadcrumbs, a list-view toggle for dense catalogues, and sections above and below the grid — the shipped template puts a trust icon bar under the products.
Collection (no filters) is an alternate template for editorial or lookbook collections: larger imagery, three columns, numbered pagination, no filter rail.
Search
Predictive search as a full-screen overlay or a drawer. It searches products, collections, queries, and optionally pages and articles. Results are keyboard navigable with the arrow keys, counts are announced to screen readers, and before anything is typed it shows the popular searches you set in theme settings.
The search results page gets the same filtering and sorting as a collection, and renders products, articles and pages differently rather than forcing everything into a product card.
Empty search and empty collection states both offer a route onward rather than a dead end.
Launch checklist
Run this before publishing. It is ordered by what costs you money if you miss it.
Content
- [ ] Every section's placeholder copy replaced. Search the site for the word "Replace" — the demo content uses it deliberately.
- [ ] Logo, mobile logo, favicon and social sharing image uploaded.
- [ ] Hero has both a desktop and a mobile image.
- [ ] Focal points set on every image that crops (Content → Files).
- [ ] Compliance disclaimer in theme settings edited for your market, or cleared.
- [ ] Before-and-after disclosure text is true of the images you used.
- [ ] Comparison table claims are ones you can substantiate.
- [ ] Statistics have a source note.
Commerce
- [ ] Search & Discovery installed, filters configured.
- [ ] Complementary products paired for your top ten SKUs.
- [ ] Free-shipping threshold matches your real policy, or the bar is off.
- [ ] Shipping, returns and privacy policies written and linked in the footer policy menu.
- [ ] Accelerated checkout buttons on (they are on by default).
- [ ] Test a real order end to end, including a discount code.
- [ ] Test the cart drawer: add, change quantity, remove, empty state.
Metafields
- [ ] The nine definitions created (
04-metafields.md). - [ ] Supplement facts filled in for every product that has a panel.
- [ ] Certificates uploaded and linked, batch and date filled in.
- [ ] Short descriptor set on every product — it appears on every card.
Technical
- [ ] Open every template: home, product, collection, collections list, cart, search, blog, article, page, contact, 404, password, gift card.
- [ ] Check at 375 px, 768 px and 1440 px.
- [ ] Tab through the header, mega menu, mobile drawer, cart drawer, search and any dialog. Focus must be visible, trapped inside dialogs, and returned to the button that opened them on Escape.
- [ ] Disable JavaScript in DevTools and confirm you can still browse, filter, and add to cart.
- [ ] Run Lighthouse on home, product and collection, mobile and desktop, with real content loaded. Aim for performance 60+ and accessibility 90+.
- [ ] Check the header at the top of the page and after scrolling, including the transparent-header setting if you use it.
- [ ] Confirm the countdown, if used, points at a real future date.
SEO and sharing
- [ ] Page titles and meta descriptions set on key pages.
- [ ] Paste a product URL into a messaging app and check the preview card.
- [ ] Test a product URL in Google's Rich Results Test.
- [ ] Submit the sitemap in Search Console after publishing.
Last
- [ ] Duplicate the current live theme as a backup before you publish.
After every upload: verify the files actually landed
Shopify's zip import can silently drop files. It reports success, the theme
appears in the theme list, processing is false, and nothing tells you a file
is missing. The only symptom is that the routes depending on those files return
404 — a product page that plainly exists, serving the 404 template.
This happened twice on this theme. Both uploads lost templates/product.json
and templates/product.landing.json, so every product page 404'd while the
homepage and collections rendered normally.
Check it in under a minute, every time:
- Sign in to the store's admin.
- Open
https://admin.shopify.com/store/<store>/themes.jsonand find the theme'sid. - Open
https://admin.shopify.com/store/<store>/themes/<id>/assets.json. - Paste
tools/verify-upload.jsinto the browser console. It prints any file that did not make it.
If files are missing: delete that theme and upload the zip again. The drop is not caused by the files themselves — the ones lost were byte-for-byte the same shape as the ones kept — so a second upload usually stores everything.
Why a missing block takes the whole page down
Shopify validates a JSON template against what it has stored. Losing one block file cascades:
blocks/product-description.liquid dropped → templates/product.json
references it → template rejected → no product template exists → every
product URL 404s, and the theme editor's template picker says "No templates
found for products".
That message in the editor is the fastest confirmation that files are missing.
The more reliable alternative
shopify theme push uploads file by file and reports failures per file. For a
theme you are shipping to customers, push rather than zip-upload, and keep the
zip for the buyer.
Developer notes
Architecture
No build step. No dependencies. Edit a file, push it, done.
assets/ base.css + nine ES modules
blocks/ 11 nestable theme blocks
config/ settings_schema.json, settings_data.json (3 presets)
layout/ theme.liquid, password.liquid
locales/ en.default.json
sections/ 49 sections + header-group.json + footer-group.json
snippets/ 43 partials
templates/ 16 templates, all JSON except gift_card.liquid
Design tokens
snippets/theme-tokens.liquid turns theme settings into CSS custom properties
inside a {% style %} block. Colour schemes are emitted as space-separated RGB
triplets so any rule can composite alpha:
color: rgb(var(--color-text));
color: rgb(var(--color-text) / 0.6);
border-color: rgb(var(--color-border));
Spacing derives from a single density multiplier, so --space-m is
calc(20px * var(--density)). Change density once and the whole site
re-proportions.
assets/base.css holds the reset, typography, layout, and every shared
component. Section-specific CSS lives in that section's {% stylesheet %}
block, which Shopify only ships for sections in the current page's render tree.
If you write a class in one file and use it in another, move it into
base.css — otherwise CSS subsetting will drop it.
JavaScript
ES modules behind an import map, declared in snippets/theme-scripts.liquid:
import { FoxforaElement, define, announce, fetchSections } from '@foxfora/core';
Every interactive part is a custom element extending FoxforaElement, which
gives you:
setup()/teardown()instead ofconnectedCallback/disconnectedCallbackthis.on(target, event, handler)— auto-removed on disconnect viaAbortSignalthis.ref('name')/this.refs('name')fordata-reflookupsthis.emit('name', detail)for document-level custom events
Because each element re-initialises itself in connectedCallback, markup
replaced by the Section Rendering API comes back alive with no re-binding.
That is deliberate: inline <script> in section-rendered HTML does not execute.
Events you can hook
| Event | Fired when | detail |
|---|---|---|
regimen:dialog:open |
Any drawer or modal opens | { id } |
regimen:dialog:close |
Any drawer or modal closes | { id } |
regimen:variant:change |
A variant is selected | { variant, sectionId } |
regimen:cart:updated |
A product form adds to the cart | { source } |
document.addEventListener('regimen:variant:change', (event) => {
console.log(event.detail.variant.id);
});
Global helper
window.Foxfora carries routes, cartType, moneyFormat, strings and
freeShippingThreshold. Always build Ajax URLs from window.Foxfora.routes.root
so they stay correct on localised and multi-market storefronts.
Cart
assets/cart.js posts to the Cart Ajax API with bundled section rendering, so
one request both mutates the cart and returns the re-rendered drawer. It sends
JSON to change.js and update.js, and the original FormData to add.js so
line item properties and selling plans pass through untouched.
Cart counts are synced from a data-cart-count-source attribute in the
re-rendered markup rather than a second request.
Section Rendering API
Used for filtering and sorting, the cart drawer, quick add, predictive search,
product recommendations, recently viewed, and variant re-rendering. Helpers in
@foxfora/core:
const sections = await fetchSections(['cart-drawer']);
const doc = await fetchDocument('/collections/all?filter.v.price.gte=10');
Accessibility contract
If you extend the theme, keep these:
- One
<h1>per page. - Every icon-only button has a
<span class="visually-hidden">label. - Every form field has a real
<label for>. - Dialogs:
role="dialog",aria-modal, focus moved in, focus trapped, Escape closes, focus returns to the opener.RegimenDialogdoes all of this — extend it rather than writing your own. - Touch targets 44 × 44 px minimum.
.button--iconalready is. - Anything that animates checks
prefersReducedMotion(). - Anything that auto-plays has a pause control.
- Dynamic updates announce through
announce(), which writes to the<a11y-announcer>live region in the layout.
Performance contract
- The LCP image is eager with
fetchpriority="high"and is never animated in. - Every image goes through
image_url+image_tagso it getssrcset,sizes, width and height. Never hand-write an<img>for content. - Build variant pickers from
product.options_with_values, never by loopingproduct.variants. - Never nest
{% render %}inside a loop more than one level deep. - Use
{% if %}to exclude markup server-side rather than hiding it with CSS.
Validating a change
npm install -g @shopify/cli
shopify theme check --fail-level error # no store needed
shopify theme package # builds the zip
shopify theme dev --store your-store # live preview
.theme-check.yml extends the recommended ruleset and raises HardcodedRoutes
and RemoteAsset to errors, with asset size budgets of 100 KB for CSS and
40 KB per JS file. Version 1.0.0 passes with zero offences.
Blocks: why the product blocks live in blocks/
The product page's block library is 38 theme blocks in blocks/, each one file
with its own schema, rendered by sections/main-product.liquid with
{% content_for 'blocks' %}.
This is not a style preference. Those blocks were originally section-local,
which put a 126 KB schema inside one 131 KB section file. Shopify stopped
serving every template that referenced that section: /products/<handle>
returned 404 while the rest of the store rendered normally, with no error
anywhere and a clean shopify theme check.
A section's schema stays small. A large block library goes in blocks/.
tools/validate.py check 22 fails the build above 40 KB of schema or 200
settings in one section — normal is 3–6 KB.
How a product block gets its context
A theme block cannot be passed variables by its section, so each one works out its own:
{%- liquid
assign target = section.settings.product
if target == blank
assign target = product
assign fid = section.id | append: '-product-form'
else
assign fid = section.id | append: '-featured-form'
endif
assign cur = target.selected_or_first_available_variant
-%}
{% render 'product-block', kind: 'title', block: block, product: target, ... %}
section.settings.product is set only by the Featured product section; on a
product template the global product object is the right one. The form id is
derived from section.id on both sides — the section and its blocks must agree
on it, and that is the one piece of coupling between them.
Every block delegates to snippets/product-block.liquid, which switches on the
kind argument. One renderer, so the product page and the Featured product
section cannot drift apart.
Adding a block type
- Add it to
tools/product_blocks.py. - Add a
{%- when 'your_kind' -%}branch tosnippets/product-block.liquid. - Run
python3 tools/emit_blocks.py— it writesblocks/product-your-kind.liquid. - Run
shopify theme checkandpython3 tools/validate.py theme.
Apps and integrations
Short answer: yes, apps work in this theme, and for most apps you do not have to touch code. This page explains the three ways a Shopify app puts content on a storefront, which of them Regimen supports, and what to do when an app wants something the theme does not offer.
1. App embeds — automatic, nothing to do
Most apps — cookie banners, chat widgets, popups, upsell overlays, analytics,
tracking pixels, currency converters, wishlists, translation bars — install as
app embed blocks. They inject themselves through Shopify's
content_for_header and content_for_layout, both of which Regimen renders in
layout/theme.liquid.
You install the app, toggle its embed on in Online Store → Themes → Customize → App embeds, and it appears. No theme edit, no developer.
This covers the large majority of the app store.
2. App blocks — supported in 14 sections
An app block is content the merchant drags into a specific place in a specific section: a review widget under the add-to-cart button, a size chart beside the variant picker, a subscription selector, a bundle builder.
A theme has to declare that it accepts them. Regimen declares @app in:
| Section | What an app block is good for there |
|---|---|
| Product | Reviews, size charts, subscriptions, bundles, financing, badges — anywhere in the buy column, in any order, at any width |
| Featured product | The same, on the homepage or a landing page |
| Apps | A full-width app section on any page. This is the general-purpose slot |
| Section (theme blocks) | An app block nested among theme blocks, at any depth |
| Rich text | An app widget inside a block of copy |
| Image banner | An app widget over a hero |
| Image with text | An app widget in the text column |
| Multicolumn | An app widget as one column |
| Testimonials | A review-app carousel in place of typed testimonials |
| Collapsible content | An app widget as one row of an accordion |
| Promo tiles | An app widget as one tile |
| Collection list | An app widget as one grid cell |
| Footer | Trust badges, newsletter apps, compliance widgets |
| Announcement bar | A shipping-bar or countdown app in the top strip |
To place one: Customize → pick the section → Add block → your app's block appears in the list under the theme's own blocks.
Where @app is deliberately not offered
Eleven sections do not accept app blocks, and this is a design decision rather than an omission. In each of them, a block is not a free-form content slot but one typed unit inside a fixed visual system:
- Marquee and Logo list — a block is one item on a scrolling track.
- Comparison table and Lab transparency — a block is one table row.
- Dosage timeline — a block is one numbered step.
- Slideshow — a block is one full-bleed slide.
- Stats bar and Icon bar — a block is one cell in a measured row.
- Ingredient spotlight — a block is one active ingredient with a dose.
- Bundle builder — a block is one pack tied to a real product variant.
- Header — a block is a mega menu attached to a named menu item.
Dropping an arbitrary app widget into a scrolling logo strip or a numbered dosage step does not produce a feature; it produces a broken layout. When an app needs to appear near one of these, put it in an Apps section directly above or below — that is what the Apps section is for, and it can go anywhere in any template.
3. Custom Liquid — the escape hatch
Every app that offers neither an embed nor a block gives you a snippet of code to paste. Regimen takes it in two places:
- Custom Liquid section — for a standalone widget anywhere in a template.
- Custom Liquid block — inside the product page's buy column, so the widget sits exactly where you want it among the other blocks.
Paste, save, done. If an app offers both a block and a code snippet, use the block: it can be moved and reordered without editing code again.
Review apps specifically
This is the most common question, so in detail.
Star ratings on product cards and the product page read Shopify's standard
product rating metafield (reviews.rating and reviews.rating_count). Every
major review app writes to it — Judge.me, Loox, Okendo, Stamped, Yotpo,
Rivyo, Fera and others. Install the app, let it sync, and the stars appear on
collection cards, search results, quick view and the product page with no theme
change at all.
If a product has no reviews, the theme renders nothing — no empty stars, no "0 reviews". That is deliberate; see the honesty rules in the README.
The review list itself — the widget with the actual written reviews — comes from the app, as an app block in the product page (preferred), or as a Custom Liquid block if the app has no block. Both are supported. A good place for it is a Tabs block in the buy column, or an Apps section below the product.
A note worth the ten seconds: most review apps offer both an app block and a paste-in snippet. Take the app block. Snippets are how a store ends up with a widget nobody can move, in a theme nobody wants to edit, two years later.
Apps that need a theme edit
A small number of apps still ask you to paste code into theme.liquid or
main-product.liquid. Before you do:
- Check the app's settings for an app embed or app block option. Most apps that still document the paste-in method also support blocks now and have not updated their instructions.
- If you must paste, duplicate the theme first. Online Store → Themes → ⋯ → Duplicate.
- Paste into a Custom Liquid block or section rather than the theme file where you can. It survives a theme update; an edited theme file does not.
What will not work
- Apps that require a specific competing theme's classes or JavaScript hooks. A few app widgets are written against Dawn's markup. Those apps generally offer a generic mode; if not, their support can usually give you a selector to target.
- Apps that replace the entire cart or checkout on a Basic plan. That is a Shopify plan limit, not a theme limit.
- Classic customer account apps. Regimen uses Shopify's hosted customer accounts; see the known limits in the README.
Changelog
Regimen follows semantic versioning. X changes break merchant settings, Y adds backwards-compatible features, Z fixes bugs without visual change.
1.1.2
sections/featured-product.liquid was still dropped on upload after 1.1.1 —
the last missing file of 185. Cause: it is the only section that accepts
@theme blocks and ships a preset, and its preset used the legacy array form
for blocks. Shopify requires keyed blocks plus block_order there (the shape it
writes itself) and rejects the file otherwise, silently. Converted.
Check 25 fails any @theme section whose preset uses the array form.
1.1.1
The real cause of the product-page 404, found by reading what Shopify stored.
Five block files carried a range setting with "unit": "". A range's unit is
optional, but an empty string is not "absent" — Shopify rejects the whole file
on upload, silently, and then rejects every template that references it.
templates/product.json referenced three of those blocks, so no product
template existed and every product URL returned 404. The theme editor said it
plainly: "No templates found for products."
This was also the cause in 1.0.1 and 1.0.2 — main-product.liquid and
featured-product.liquid each carried five empty units and were dropped whole.
The product page worked in 1.0.0, before the block generator existed. The
schema-size explanation in 1.1.0 was wrong; the theme-block architecture it
produced is kept because it is right on its own merits.
Proved, not inferred: on a fresh upload the same eight files went missing, and the only schema feature present in every dropped block and absent from every stored one was the empty unit.
Fixed
tools/product_blocks.py:rng()omitsunitwhen it is empty.- All five blocks regenerated; no empty unit anywhere in the theme.
New check
- 24 — empty-string attributes. Fails any
unitorplaceholderset to"". Proved against the exact dropped file.
Upload procedure
tools/verify-upload.js + docs/07-launch-checklist.md: after every upload,
diff the store's asset list against the package. It is a one-minute check that
turns a silent 404 into a named file.
1.1.0
The product page returned 404 on every product. The cause, the fix, and the check that makes it impossible to ship again.
The bug
sections/main-product.liquid carried a 126 KB schema — 39 block types and 410
block settings inlined into one section, in a 131 KB Liquid file. Shopify
stopped serving every template that referenced it. /products/<handle> returned
a plain 404 while /collections/all rendered normally and
/products/<handle>.js returned the product, because the product was fine — the
template was not.
Nothing reported it. shopify theme check: clean. Theme upload: accepted. No
error page, no log line. The only visible symptom was a 404 on a page that
obviously exists.
It was isolated by elimination: every section on the working homepage has a
3–6 KB schema, main-product had 126 KB, and the only broken route was the only
template that referenced it. featured-product was the same size and broke
nothing — because no template used it.
The fix — block library moved to theme blocks
The 38 product blocks are now theme blocks in blocks/, one file per block with
its own small schema, rendered with {% content_for 'blocks' %}.
main-product.liquid: 131 KB → 12 KBfeatured-product.liquid: 127 KB → 8 KB- Largest section in the theme: 15 KB, 6% of Shopify's 256 KB file limit
- Nothing was removed. 452 block settings, and the blocks now work in the Featured product section and the product page from one definition.
Breaking
The product page's blocks are re-created on upload. Block types changed from
section-local (title) to theme blocks (product-title), so any customisation
made to the product page in the theme editor has to be redone. Every other
template and all theme settings are untouched.
Also fixed
- Footer menus were invisible and unclickable. On desktop the CSS hid the
<summary>of each<details>— but hiding a summary does not open a details, so the whole column rendered as blank space that the editor still listed. The panels now ship open and collapse into accordions below 750px from JavaScript, which is the only thing that can open a<details>. With JavaScript off, every panel stays open rather than disappearing.
New checks
- 22 — section schema weight. Fails above 40 KB of schema or 200 settings in one section. Normal is 3–6 KB; this broke at 126 KB.
- 23 — block targets. Every block type in a JSON template must resolve to a
declared section block or a real file in
blocks/, and a section accepting@thememust actually rendercontent_for 'blocks'.
Both are proved against the code that was broken.
1.0.2
Three Liquid faults that shopify theme check reports as clean. All three are
now caught by tools/validate.py, and each new check is proved against the
exact line that was broken.
Fixed
snippets/structured-data.liquidkilled every page. The schema.org SearchAction target put{search_term_string}inside a{{ }}output tag. Shopify's tokenizer reads that brace as the start of a new tag and raises "was not properly terminated with regexp: /}}/", which takes the whole snippet down and prints the error above the header. The placeholder now sits in the raw JSON text, outside any Liquid tag. → validator check 19sections/footer.liquidprinted a Liquid error instead of the copyright.| t: year: 'now' | date: '%Y', shop: shop.name— a filter chain cannot sit inside another filter's argument list, sodatereceived three arguments. The year is assigned first. → validator check 18- 23 images rendered as literal
<img …>text. Everyimage_tagcall ending inalt: something | escapehanded the trailing filter the generated markup rather than the alt text, HTML-escaping the whole tag. Verified against a Liquid engine before changing anything. Alt text is now assigned first, everywhere. → validator check 21 - The slideshow silently lost its
srcsetandsizes. The same fault in a different position:loading: forloop.first | default: nil, widths: …meant every argument after the inner filter was discarded, so the hero image shipped one 2400px file to phones. The first slide now loads eagerly withfetchpriority: high; the rest stay lazy. - Full-width sections put text against the viewport edge.
.page-width--fullwas zeroing the gutter along with the max-width. It keeps the gutter now; the section background still bleeds edge to edge because it sits on the section wrapper..page-width--bleedcovers the rare case that genuinely wants zero inset.
Changed
- Buttons with no link no longer render. Eleven copies of button markup
became one
snippets/button.liquid. A blank link used to fall back tohref="#", which looks like a working control and jumps the page to the top. A bare word typed into a link field ("shop") now gets a leading slash instead of 404ing from every page but the root. Every link setting says so in the editor. - Promo tiles are an anchor only when they have a link, a
divotherwise.
1.0.1
Fixes and additions from the first live upload. Nothing here breaks a merchant's
existing settings except the full_width checkbox noted below, which the theme
migrates automatically in its own templates.
Fixed
- The header and footer section groups used hyphenated block IDs. Shopify
accepts only alphanumeric characters in a section or block ID. The theme
uploaded and passed
shopify theme checkbut the theme editor rejected those blocks at runtime, which is why the announcement bar errored. Nine IDs renamed.tools/validate.pynow checks for this; theme check does not. - The announcement bar rendered at label size (~11.5px) with no way to change it. Rebuilt with three modes (rotate, marquee, static), a text size range, three layouts, arrows, a marquee pause control, session-remembered dismissal, and per-message icons and colour schemes.
- Half-width product blocks could never sit side by side.
base.cssset.product-info { flex-direction: column }, which overrode the section's own wrap rule. Nowflex-flow: row wrap. - The product block layout CSS lived in
main-product's section stylesheet, so the Featured product section rendered its blocks unstyled everywhere except the product page. Moved tobase.css, where all three block-rendering sections can reach it. - Product tabs had no Home or End key handling. Added, per the WAI-ARIA tabs pattern.
- Generated schemas were approaching Shopify's 256 KB per-file limit at 63%. Reformatted (whitespace only) to 51%.
Added
- The product page went from about 30 options to 482 block settings across 39 block types, each with its own Arrangement group: width, alignment, space above and below, a rule above, keep-side-by-side-on-mobile, and hide on mobile or desktop. New block types: stock bar, selling plan picker, line item property, back-in-stock request, delivery estimate, countdown, icon row, payment icons, tabs, metafield text, heading, text, image, video, divider, complementary products, full-details link.
- An "About this section" note on every one of the 50 sections — what it is for, an example, and a tip — readable in the theme editor without leaving it.
- Width and content-alignment controls, and separate mobile padding, on every
placeable section. Four sections that had a
full_widthcheckbox now have a three-way width select instead, which also gives them a narrow option. @appblocks in 14 sections, up from 5. Seedocs/09-apps-and-integrations.mdfor which, and why not the rest.- Anchor ID and custom CSS class on every placeable section.
- A footer theme-credit setting ("Theme by Foxfora", on by default).
tools/: the block-library generator, a 17-check structural validator, and a headless-browser harness that makes 38 behavioural assertions.
Renamed
- The theme is Regimen by Foxfora. The JavaScript namespace is
foxfora(window.Foxfora,@foxfora/*,FoxforaElement,foxfora-dialogand friends) so future themes share it without another rename.
1.0.0
First release.
Storefront
- Header with three layouts, sticky modes, transparent-over-hero, three-level nested menus and a mega menu with columns, a promo tile and featured products.
- Rotating announcement bar with optional phone number and country selector.
- Mobile navigation drawer with drill-down panels and focus management.
- Cart drawer and cart page, free-shipping progress, order notes, complementary product recommendations, optional terms gate.
- Predictive search as a full-screen overlay or drawer, with popular searches, keyboard navigation and live result announcements.
- Faceted filtering on collection and search, sidebar or horizontal, with active-filter chips, per-value counts, price range, swatch filters, and Section Rendering API updates that keep the browser back button working.
- Product page with four gallery layouts, image lightbox, native colour and image swatches, quantity rules and volume pricing, pickup availability, Shop Pay Installments, Follow on Shop, sticky add-to-cart bar, and a block for every element.
- Quick add and quick view from any product grid.
- Blog, article with comments and prev/next, 404 with search, password page, gift card with QR and Apple Wallet.
Wellness features
- Supplement facts panel driven by a JSON metafield, with a rich-text fallback.
- Ingredient spotlight: per-active dose, source and reference link.
- Testing transparency panel and a per-product certificate of analysis block.
- Routine timeline for dosing schedules and expected-results periods.
- Pack picker that reads real product variants, so prices and savings are true.
- Dietary label row, how-to-use steps, comparison table, before and after slider with a mandatory disclosure field.
System
- Six-scheme colour system with paired foregrounds, applied per section and per block.
- Three-font typography system (heading, body, label) with fluid
clamp()scales, per-role tracking, leading and case. - Layout system: page width, spacing density, grid gap, three radius tokens, border width, shadow.
- Animation system with scroll reveal, stagger, hover effects, and full
prefers-reduced-motionsupport. - Structured data for Organization, WebSite with SearchAction, Product with offers and AggregateRating, Article, BreadcrumbList.
- Open Graph and Twitter cards,
page_imagesupport, favicon setting. @appblocks in the main product, featured product, footer and Apps sections; Custom Liquid available as both a section and a block.- Works with JavaScript disabled: browse, filter, add to cart and navigate.
Regimen theme licence
Copyright (c) 2026 Foxfora. All rights reserved.
Regimen is original software. It is not derived from, and does not incorporate any portion of, Shopify's Dawn, Horizon or Skeleton themes, or any other third-party theme or library.
Single store licence
When you buy one licence you may:
- Install and use Regimen on one Shopify store you own or operate, plus any number of development, staging and preview stores for that same store.
- Modify the theme's code and content however you like, for that store.
- Keep using the version you bought indefinitely, with no recurring fee.
You may not:
- Install it on a second production store without a second licence.
- Resell, relicense, sublicense, redistribute or give away the theme files, in original or modified form.
- Publish the theme files, or a derivative of them, to any marketplace, theme shop, repository or file-sharing service.
- Remove or obscure this licence file from your copy.
Agency and multi-store licences
If you build stores for clients, each client store needs its own licence. A multi-store or unlimited-store licence is available; contact support.
Third-party content
Regimen's code is wholly original. Any demo photography, fonts or copy shown on a demo store is licensed to the demo store only and is not included with the theme. Supply your own images, fonts and copy.
No warranty
Regimen is provided "as is", without warranty of any kind, express or implied. The copyright holder is not liable for any claim, damages or other liability arising from the use of the theme.
Compliance is yours
Regimen includes fields for supplement facts, ingredient doses, lab results and health-adjacent claims. It renders exactly what you type. Whether a claim is permitted where you sell, and whether a figure is accurate, is entirely your responsibility. The theme deliberately cannot invent any of it.
Stuck? Support is included.
Email replies within two business days for 12 months from purchase — install help, bugs, settings questions and documentation gaps.
Get support →