Regimen by Foxfora · Documentation

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:

  1. Hero
  2. Trust icon bar
  3. Best sellers
  4. Shop by goal
  5. Ingredient spotlight
  6. Brand story
  7. Routine timeline
  8. Testing transparency
  9. Reviews
  10. Comparison table
  11. FAQ
  12. Journal
  13. 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.

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:

  1. Sign in to the store's admin.
  2. Open https://admin.shopify.com/store/<store>/themes.json and find the theme's id.
  3. Open https://admin.shopify.com/store/<store>/themes/<id>/assets.json.
  4. Paste tools/verify-upload.js into 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 of connectedCallback / disconnectedCallback
  • this.on(target, event, handler) — auto-removed on disconnect via AbortSignal
  • this.ref('name') / this.refs('name') for data-ref lookups
  • this.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. RegimenDialog does all of this — extend it rather than writing your own.
  • Touch targets 44 × 44 px minimum. .button--icon already 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_tag so it gets srcset, sizes, width and height. Never hand-write an <img> for content.
  • Build variant pickers from product.options_with_values, never by looping product.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

  1. Add it to tools/product_blocks.py.
  2. Add a {%- when 'your_kind' -%} branch to snippets/product-block.liquid.
  3. Run python3 tools/emit_blocks.py — it writes blocks/product-your-kind.liquid.
  4. Run shopify theme check and python3 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:

  1. 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.
  2. If you must paste, duplicate the theme first. Online Store → Themes → ⋯ → Duplicate.
  3. 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() omits unit when it is empty.
  • All five blocks regenerated; no empty unit anywhere in the theme.

New check

  • 24 — empty-string attributes. Fails any unit or placeholder set 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 KB
  • featured-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 @theme must actually render content_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.liquid killed 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 19
  • sections/footer.liquid printed 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, so date received three arguments. The year is assigned first. → validator check 18
  • 23 images rendered as literal <img …> text. Every image_tag call ending in alt: something | escape handed 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 srcset and sizes. 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 with fetchpriority: high; the rest stay lazy.
  • Full-width sections put text against the viewport edge. .page-width--full was 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--bleed covers 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 to href="#", 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 div otherwise.

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 check but the theme editor rejected those blocks at runtime, which is why the announcement bar errored. Nine IDs renamed. tools/validate.py now 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.css set .product-info { flex-direction: column }, which overrode the section's own wrap rule. Now flex-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 to base.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_width checkbox now have a three-way width select instead, which also gives them a narrow option.
  • @app blocks in 14 sections, up from 5. See docs/09-apps-and-integrations.md for 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-dialog and 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-motion support.
  • Structured data for Organization, WebSite with SearchAction, Product with offers and AggregateRating, Article, BreadcrumbList.
  • Open Graph and Twitter cards, page_image support, favicon setting.
  • @app blocks 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 →