Skip to content

Bundle anatomy

Required files, the layout/block contract, reserved paths, and the CSS and asset rules.

Updated 2 Sept 20262 min read
On this page

This page is for anyone organizing a theme's files. By the end you know what is required, how a template binds to the layout, and what the server rejects at push and why.

Files#

Path Required Purpose
layout/theme.liquid yes the shell every page renders into
config/settings_schema.json yes the settings and sections schema; see Settings schema
templates/home.liquid in practice, yes the home entry point; the smoke render at push starts there
templates/product.liquid, templates/category.liquid, templates/cart.liquid per page you support see Templates
snippets/section-body.liquid if the schema declares sections the section body dispatcher; see Sections
snippets/*.liquid no partials included by full path
locales/ar.json and locales/en.json recommended tables for the t filter; see Arabic first
assets/* no CSS, images, fonts
config/showcase.json no the showcase and first-install document for sectioned themes

Allowed extensions only: .liquid, .json, .css, .svg, .png, .jpg, .jpeg, .webp, .woff2. Any .js, .ts or .html is rejected; a theme ships no JavaScript at all, and interactivity comes from the platform runtime through data-dukkan-* attributes. Numeric limits are in Limits.

The layout/block contract#

The layout declares a content block and every template fills it:

layout/theme.liquidLiquid
<!doctype html>
<html dir="{{ request.direction }}" lang="{{ request.locale }}">
<head>
  <meta charset="utf-8">
  <title>{{ store.name }}</title>
  <style>{% include 'assets/theme.css' %}</style>
</head>
<body>
  {% block content %}{% endblock %}
</body>
</html>
templates/product.liquidLiquid
{% layout 'layout/theme.liquid' %}
{% block content %}
  <h1>{{ product.name }}</h1>
  <p>{{ product.price | money }}</p>
{% endblock %}
  • There is no content variable inside the layout; anything outside {% block %} in a template is not rendered.
  • A layout may declare several blocks (head, content, scripts_data) and a template fills whichever it wants.
  • {% include %} takes the full path inside the bundle: {% include 'snippets/product-grid.liquid' %}. There is no directory lookup or implicit extension, and no path outside the bundle ever resolves.

Reserved paths and attributes#

  • snippets/sections.liquid is platform-reserved: the platform injects it at push, and its presence in your bundle is rejected. You write snippets/section-body.liquid only.
  • Never emit data-dukkan-section or data-section-id= from your own Liquid; the wrapper is platform-owned, and it is rejected by the static scan and in the output of the smoke render.
  • Capability attributes such as data-dukkan-add-to-cart and data-dukkan-cart-count are allowed; they are your contract with the runtime.

Liquid rules#

A template is rejected at push if it contains any of: <script> (except <script type="application/ld+json"> for JSON-LD data), an inline event handler (onclick=), javascript:, data:text/html, <iframe>, or the | raw filter. Verbatim error strings are in Theme validation.

Every {{ }} output is escaped automatically; the only path to raw HTML is a platform filter that marks its output safe (money, money_major, json, richtext). See Filters.

CSS and assets#

  • Put styles in assets/theme.css and inline them in <style> via {% include 'assets/theme.css' %}; CSS files are scanned like templates, and any < inside them is rejected (it forecloses a </style> breakout).
  • Use logical properties (padding-inline, margin-inline-start) rather than left/right; see Arabic first.
  • Prefix class names with your theme (.bz-card) so two installed themes never collide inside saved settings.
  • Merchant images and files arrive through asset_url and img_url, never as absolute URLs. Theme images bundled under assets/ are stored and served from storage.

No money math in Liquid#

Prices are { amount_minor, currency } objects. Display them with money, and use the platform-computed product.savings and section.products_total instead of subtracting or adding in the template. See Objects.