Getting started

Themes are sandboxed Liquid — data-only templates rendered server-side against a fixed data contract. Themes ship zero JavaScript: cart, item options and checkout come from the platform commerce runtime, which your markup binds to declaratively.

Download the starter theme (.zip)

  1. Download and unzip the starter.
  2. Rename the slug in theme.json.
  3. Edit sections in sections/; add snippets in snippets/.
  4. Zip the folder contents and submit it for review.

Package layout

mytheme.zip
├── theme.json                  # manifest (data, never code)
├── templates/index.json        # OS 2.0 sectioned template (or index.liquid single-file)
├── sections/*.liquid           # section files, each with its schema block
├── snippets/*.liquid           # reusable partials for the render tag
├── config/settings_schema.json # theme-level settings (Shopify-style groups)
├── locales/en.json …           # theme strings for the t filter
├── assets/                     # css / images / fonts — static only
└── preview.png                 # listing screenshot

Caps: 10 MB Zip 2 MB per file, folder depth ≤ 3. Allowed file types: .liquid .json .css .png .jpg .jpeg .webp .svg .woff2. No PHP. No JavaScript. SVGs must contain no script or foreignObject.

theme.json Manifest

{
  "name": "My Theme",
  "slug": "my-theme",          // letters/numbers/dashes — becomes the install path
  "version": "1.0.0",          // published versions are immutable; ship updates as new versions
  "author": "You",
  "description": "…",
  "min_platform_version": "2.0"
}

Templates & sections (OS 2.0)

templates/index.json lists section instances; each section is a Liquid file in sections/ carrying its settings schema in a schema block. Limits: ≤ 25 sections per template, ≤ 50 blocks per section.

// templates/index.json
{
  "sections": {
    "hero":  { "type": "hero", "settings": { "heading": "Welcome" } },
    "menu":  { "type": "menu-grid",
               "blocks": { "b1": { "type": "badge", "settings": { "label": "New" } } },
               "block_order": ["b1"] }
  },
  "order": ["hero", "menu"]
}

Inside a section file you receive section.id, section.type, section.settings.* and section.blocks (each block: id / type / settings.*). Setting types: text, textarea, color, checkbox, select (Options), range (min/max), image_picker.

Data — the Drop reference

Templates can reach only the properties below (generated from the platform's Drop classes — this table cannot drift). Globals: restaurant, options, settings, menu, allergies, banners, branches, active_branch, table, customer, localization, flags, stats, and section inside section files. Anything else renders blank (and errors at upload).

AllergyDrop

id int image string title string

BannerDrop

id int image string image_url string link_url string subtitle string title string

BlockDrop

id string settings App\Storefront\Drops\SettingsDrop type string

BranchDrop

accepts_orders bool id int is_open bool name string status string

CategoryDrop

id int image_url string items array name string

CustomerDrop

name string phone string store_credit float store_credit_formatted string

ExtraDrop

id int name string price float

FlagsDrop

allow_order bool delivery bool on_table bool payment bool scheduling bool takeaway bool

ItemDrop

description string dietary_tags array extras array has_variants bool id int image string is_daily_special bool is_gluten_free bool is_halal bool is_popular bool is_sold_out bool is_vegan bool name string option_groups array price float rating float rating_count int variants array

LanguageDrop

code string direction string name string

LocalizationDrop

currencies array current App\Storefront\Drops\LanguageDrop direction string languages array

MenuDrop

categories array is_empty bool items array

OptionGroupDrop

choices array id int max_select int min_select int name string required bool

OptionsDrop

allow_call_waiter bool allow_coupons bool allow_dietary_filters bool allow_multi_branch_switch bool allow_order_scheduling bool allow_tips bool currency_code string currency_pos string currency_sign string customer_auth_mode string delivery_charge float enable_multi_currency bool menu_sections string min_order_value float open_close_store bool tax_charge float tax_label string whatsapp_number string

RestaurantDrop

address string color string cover string description string id int logo string main_image string phone string slug string sub_title string title string

SectionDrop

blocks array id string settings App\Storefront\Drops\SettingsDrop type string

SettingsDrop

StatsDrop

scans_today int

TableDrop

id int table_no string

VariantDrop

id int name string price float

Filters & tags

Platform filters (plus the standard safe Liquid set — escape, date, where, map, sort, size…):

media_urlmoneymoney_codettheme_asset

  • t — translate a key. Read-only; unknown keys return the key itself.
  • money / money_code — format a price with the restaurant's currency.
  • media_url — restaurant image URL: {{ item.image | media_url: 'menu' }} (types: menu, logo, cover, allergy, banner).
  • theme_asset — URL of one of your bundled assets: {{ 'css/style.css' | theme_asset }}.

Tags schema (section settings, stripped from output), render (snippets by name — your snippets/ dir only), commerce (emits the commerce runtime once per page). Unknown filters/tags fail at upload.

Commerce runtime

Drop {% commerce %} once (usually at the end of your template), then bind with data attributes — the runtime owns cart state, the option sheet and checkout:

<button data-mb-add="{{ item.id }}">Add</button>
<span   data-mb-cart-count></span>
<span   data-mb-cart-total></span>
<button data-mb-open-cart>Cart</button>
<button data-mb-call-waiter>Call waiter</button>
<div    data-mb-item-sheet-mount hidden></div>
<div    data-mb-cart-mount hidden></div>

Skin the runtime UI via its .mb-* classes and CSS custom properties (--mb-sheet-bg, --mb-sheet-ink, --mb-accent).

Merchant settings

Merchants customize your theme in the dashboard against your schemas. Precedence: schema defaults ← template values ← merchant values. Settings are stored independently of the theme version, so shipping an update never wipes a merchant's customization.

Rules & limits

  • No <script>, no inline event handlers, no javascript: URLs, no iframes — rejected at upload.
  • Escape user-visible data: {{ item.name | escape }}.
  • Renders are resource-bounded (output length + engine work caps). A section that fails at runtime is skipped, never a blank page; at upload it is a hard error.
  • Published versions are immutable — updates are new versions through review.
  • Use logical CSS properties (inline-size, margin-inline…) — menus render RTL too.

Contact Us

Follow Us