THEMES

Building a theme

A theme is a single JSON file. It controls how a storefront looks — palette, display font, header menu, home banner and footer. It never contains products, categories, orders or settings, which is why one file works in any store.

Anyone can write one by hand. Nothing needs to be compiled.


The file

{
  "format": "my-store.theme",
  "version": 1,
  "theme": {
    "name": "Midnight",
    "fontFamily": "Space Grotesk",
    "colors": { "...": "9 tokens" },
    "menu": [ { "label": "New Arrivals", "href": "/new-arrivals" } ],
    "hero": { "...": "banner image and copy" },
    "footer": { "...": "columns and small print" }
  }
}

format must be exactly my-store.theme — it is what stops an unrelated JSON file being imported. version is optional today; bump it only when a future change makes old files unreadable.

Import rejects anything that fails validation, field by field, so a mistake gives you a message like theme.colors.ink must be a hexadecimal color rather than a broken shop.


Colours

All nine are required, all hex (#RRGGBB or #RGB).

Token What it paints Weight
ink text, dark buttons, footer, hero backdrop heaviest — 63 uses
accent brand highlight: logo dot, badges, hover states, newsletter band 36
cream raised surfaces, light buttons 23
clay image placeholders while photography is missing 14
rust the "AUTUMN 26" eyebrow chip 7
sand page background 4
parchment text on rust 4
dune the ticker strip under the hero 3
gold hover state on the hero's primary button 2

Two pairings carry most of the design: ink on sand for the page, and accent for anything interactive. Get those right and the rest follow.

Watch the contrast pairs — the storefront assumes:

  • cream and parchment read against ink
  • ink reads against sand, cream and clay
  • white reads against accent (badges and the newsletter band use white text)

An accent lighter than about #999 will fail that last one.


Font

"fontFamily": "Space Grotesk"

Any Google Fonts family name, letters, digits and spaces only. The storefront loads weights 400 and 700, so pick a family that has both. The name must match Google's exactly — DM Sans, not DM-Sans.


Up to 12 items. Each needs a label (1–40 chars) and an href.

{ "label": "Sale", "href": "/new-arrivals", "highlight": true }

highlight renders that one link in the accent colour — the design uses it for "Sale".

href must be a relative path starting with /. Absolute URLs are rejected; a theme cannot point a shop's navigation at another site.

Real paths: /, /new-arrivals, /wishlist, /bag, /search, /discover, /account.


Hero

Every field is optional; anything omitted falls back to a default.

Field Limit Notes
eyebrow 80 small chip above the headline
title 160 \n becomes a line break
subtitle 400 hidden if empty
primaryLabel / primaryHref 40 / 200 main button
secondaryLabel / secondaryHref 40 / 200 empty label hides the button
url 1024 desktop banner, must be https://
mobileUrl 1024 portrait crop; falls back to url

Leave url and mobileUrl as null for a portable theme. A URL only resolves on the deployment it came from; elsewhere the banner shows its placeholder and the colours, font and copy still work.


Up to 6 columns, each with up to 12 links. Same href rule as the menu.

"footer": {
  "tagline": "Your style, your rules.",
  "columns": [
    { "title": "SHOP", "links": [ { "label": "New Arrivals", "href": "/new-arrivals" } ] }
  ],
  "legal": "© 2026 My Store",
  "social": "Instagram · TikTok"
}

The storefront lays the columns out automatically, so 2 columns and 6 both look deliberate.


Two ways to build one

Fork an existing theme — the quickest route. In the admin, open Themes, press + NEW THEME (it starts as a copy of the live design), change what you want with the preview beside you, save, then EXPORT.

Write the JSON directly — export any theme as a starting point, edit it, and import it back. Import creates it inactive, so you can check it in the gallery before making it live.


Before you ship a theme

  • Import it into an empty store — one with no products and no uploaded images. That is how a stranger will first see it, and it catches themes that only look right against your own catalogue.
  • Check the header, hero, product row, newsletter band and footer. The gallery card preview shows all of them at once.
  • Check both surfaces: the storefront is a single responsive app, and the mobile layout uses the same tokens differently.
  • Set hero.url to null unless you are shipping the theme inside the same deployment as the image.
  • Give it a distinctive name. On import, a clash is suffixed (Midnight (imported)), never overwritten — but a clear name saves the recipient renaming it.

Limits at a glance

Field Rule
format exactly my-store.theme
name 1–80 characters
fontFamily 1–80, letters/digits/spaces, a real Google Fonts family
colors.* all 9 required, valid hex
menu ≤ 12 items; label 1–40; href relative, starts with /
footer.columns ≤ 6; title 1–40
footer.columns[].links ≤ 12; label 1–40; href relative
hero.url, hero.mobileUrl https:// or null