Skip to main content
A ThemeDocument is one JSON object: a name, a map of pages, blocks that repeat on every page, and one theme object for tokens.
This page follows the ThemeDocument schema in packages/builder-contracts and the route resolver that serves it. It covers the Easy lane only. See Themes for how Easy compares to an Advanced code storefront.

The shape of a document

A minimal document looks like this:
A document has hard limits. Element nesting can go up to 12 levels deep, and a single node can have up to 200 children. A page renders up to 2,000 nodes. That count includes its own blocks plus every global block. The whole document, as serialized JSON, cannot pass 2,000,000 bytes.

Page IDs

Every entry in pages has an ID. Some IDs are built in and reserved. A custom page created in the Builder gets its own ID instead, and cannot reuse a reserved one. Shoppex rejects the collision at conversion time. A merchant-created custom page can hold four block types only: custom-html, image, media-embed, and text-block. Shoppex converts it into a normal ThemeDocument page, keyed by the same ID the merchant chose.

Which page renders which URL

Shoppex turns an incoming URL path into a page ID before it renders anything. A page can add more paths in its own aliases field. Shoppex checks pages in document order and returns the first match, so an earlier page’s alias can win over a later page’s own slug. Shoppex has no direct URL for not-found. It serves that page, with a 404 status, only when no other route matches.

Blocks

See the Block reference for every block type and every setting it reads. A block’s children array holds a small set of element types:
  • heading, text, button, image, icon
  • spacer, divider
  • countdown, marquee, accordion, tabs, before-after, carousel, stat-counter, flip-card
  • group, which nests other elements inside it, up to the depth and count limits above

Design tokens and style slots

theme.style_slots is a flat map from a slot name to a value. A fixed set of core slots covers color, radius, spacing, and type: A responsive number takes a base value plus optional overrides at the sm, md, lg, and xl breakpoints, for example { "base": 14, "lg": 16 }. A slot name that starts with theme., for example theme.hero.overlayOpacity, is scheme-specific and free-form. Its value can be a color, a string, a number, a boolean, or a responsive number or string. theme.presets is an array of named style bundles. Each preset targets one element type. It can set values only for these CSS properties:
  • fontSize, fontWeight, color, backgroundColor
  • padding, paddingTop, paddingRight, paddingBottom, paddingLeft
  • margin, marginTop, marginRight, marginBottom, marginLeft
  • borderRadius, letterSpacing, lineHeight, textTransform
An element in children picks up a preset through its own preset field, by ID. theme.custom_css is merchant CSS applied after the scheme’s own styles, up to 65,536 bytes (64 KB). Shoppex validates it before saving: media, supports, font-face, keyframes, -webkit-keyframes, layer, container, scope, page, and property at-rules are allowed. The behavior and -moz-binding properties are not, and neither are the expression(), image-set(), or -webkit-image-set() functions.

Revisions

A publish turns the current draft into a new revision. A published revision never changes again. See Visual themes for the full editing loop, and Themes for what a publish does across both storefront lanes.