Build themes for WhizzyCommerce

A theme is JSON and CSS. No build step, no framework, no server to run. The same function that decides whether your theme is valid on our side runs on your laptop, so a clean check here is a clean upload there.

Start

whizzy-theme init my-theme
whizzy-theme validate my-theme
whizzy-theme pack my-theme

init writes a complete, working theme, scaffolded from the one we ship ourselves. It validates before you have touched it, which means every error after that is yours and findable.

The folder

my-theme/
  theme.json      identity, design tokens, theme settings
  theme.css       styling for the whole theme
  sections/       one file per section: its form, its data, its markup
  templates/      one file per page: which sections, in which order
                  header · home · collection · product · footer

Those five pages are the whole set. A template named anything else is reported when you validate, rather than installing quietly and drawing nothing.

Send us the folder as a zip when it is ready. Keeping everything in theme.json works too, the folders are for your comfort, not a second format.

A section, whole

This is a real one. Four parts: what the merchant sees in the editor, the form they fill in, the CSS, and the markup.

{
  "type": "my-theme.ticker",
  "label": "Ticker",
  "category": "layout",
  "description": "Short phrases across the page, in the accent colour.",

  "blocks": [{
    "type": "phrase", "label": "Phrase", "max": 6,
    "fields": [
      { "key": "text", "label": "Text", "type": "text", "default": "Free delivery" }
    ]
  }],

  "css": ".band { display: flex; gap: 3rem; background: var(--shop-accent) }",

  "render": [
    { "node": "box", "as": "section", "cls": "band", "children": [
      { "node": "slot", "children": [
        { "node": "text", "as": "span", "value": { "get": "block.text" } }
      ]}
    ]}
  ]
}

Declaring blocks is what gives the merchant an “Add phrase” button, capped at six. You never build that form. slot draws its children once per block, which is what makes block.text mean this phrase.

Put "$schema": "https://whizzycommerce.com/schema/theme-v1.json" at the top of theme.json and your editor will complete all of this and explain each field inline. That schema is generated from the validator itself, so it cannot disagree with what we accept.

Two things to know before you start

A theme ships no JavaScript. None, in any form. That is the reason a theme written by somebody we have never met can be installed on a live shop at all: a section is data, and our storefront draws it, so there is nothing to sandbox because there is nothing that runs. Anything interactive comes from a component you place by name, and four of them take their layout from your theme, so an accordion in your theme looks like your accordion. If you need a behaviour that is not in the list, tell us: that is a gap on our side, and it is how the list has grown.

Styling is plain CSS over our tokens, not Tailwind. Write var(--shop-accent), var(--shop-font-heading), var(--shop-radius). Your theme.json sets the defaults and the merchant can change any of them without touching your CSS. Your class names are scoped and prefixed when the theme is installed, so .card in your theme cannot collide with anybody else's. Properties are checked against a list; whatever is refused is reported when you validate, never dropped in silence.

Write it so it can be translated

Anything a merchant might reword should be a field with a default, not a string written into the markup. A field appears in their editor and in their translation screen, so your English heading becomes their Portuguese one. A literal is stuck in English on every shop that installs your theme.

The reference

Every node, component, data source, icon, token and limit, generated from the rules themselves:

Theme referenceJSON Schema

Build themes · WhizzyCommerce