Theme contract

What the platform guarantees a theme, what the theme must do, and how each rule is checked.

The theme contract is what Tringify guarantees every hosted theme, and what a theme must do in return. Each rule below gives both sides. Where a gate is listed, a theme that breaks the rule is refused: themecheck gates run at upload, Theme Store publish and code save, and server gates refuse the request on the store. Advisory checks report a likely breach without refusing the theme. The Store MCP get_theme_contract tool returns the same rules, every ctx field and the setting types.

Phone numbers#

phone.countries#

The platform guarantees

The theme must

phone_countries lists the store's allowed phone countries, sorted by name in the shopper's language, each with a mobile sample number and min_digits/max_digits (digits after the dial code).

Build country pickers and phone fields only from phone_countries, never from bundled country data.

Gate: none; the rule is not checked automatically. Advisory checks: themecheck bundled-countries warns at upload, Theme Store publish and code save. Fields: phone_countries*. Details: Customer and forms

phone.default#

The platform guarantees

The theme must

customer_settings.default_phone_country is the merchant's choice, otherwise the shopper's selected country, otherwise the country they visit from, always inside the allowed list.

Start phone fields on default_phone_country, not on the first list entry.

Gate: none; the rule is not checked automatically. Fields: customer_settings.default_phone_country. Details: Customer and forms

phone.submit#

The platform guarantees

The theme must

Every phone input (sign-in, registration, add phone, addresses, contact form, back-in-stock, checkout) is checked on the server: valid for its country, inside the allowed list and stored as E.164. An invalid number is refused with a translated message. A Gift Card recipient's phone follows gift_card.recipient.

Send one E.164 number (+ and digits). Bounding typing to max_digits is a courtesy, never the check.

Gate: the platform refuses with STOREFRONT_CUST_INVALID_PHONE or AUTH_PHONE_COUNTRY_NOT_ALLOWED (sign-in, registration, add phone, addresses, contact form, back-in-stock and checkout). Fields: forms.contact.phone*. Details: Customer accounts

phone.display#

The platform guarantees

The theme must

Every phone a theme shows has phone_display beside it: the number in its country's international format.

Show phone_display. Never show the raw phone or format numbers in the theme.

Gate: none; the rule is not checked automatically. Advisory checks: themecheck phone-display warns at upload, Theme Store publish and code save. Fields: customer.phone*, customer.addresses.items[].phone*, customer.order.contact.phone*, address.phone*, cart.items[].gift_card_recipient.phone*. Details: Customer and forms

Money#

money.formatted#

The platform guarantees

The theme must

Every price, total and amount comes formatted for the page language and the shopper's country (en-IN groups in lakhs).

Output the formatted value, or the price through the money filter. Never format, convert or do arithmetic on money in the theme, and never build a price by joining a symbol and a number.

Gate: none; the rule is not checked automatically. Advisory checks: themecheck money-arithmetic, money-filter, money-symbol, money-decimals warn at upload, Theme Store publish and code save. Fields: price.*, price_range.*, money_input.*, presented_money.*, *.formatted. Details: Money and languages

money.note#

The platform guarantees

The theme must

product.price_notes and cart.price_notes are the market's three shopper notes for the product price and the cart total, each finished and in the shopper's language: tax (included, estimated, excluded or calculated at checkout; null where no tax is charged), shipping (calculated at checkout or free, when something shown is delivered) and duty (who pays import duties when an item ships into the shopper's country: on delivery, at checkout or included). Each is null when it does not apply or the merchant emptied it. They follow the market's setup for the shopper's country; merchants word them in Markets (Tax, Shipping and Duties). product.price_note and cart.price_note are the same notes joined into one sentence (the product's without shipping).

Print the notes as given with the product price and the cart total (on every cart surface), and print nothing for a null note. Let the merchant choose where they show and which of the three show (a Price notes block with tax, shipping and duty choices), and show them by default. Never override their text, and do not compose tax, shipping or duty notes from tax_display or your own locale keys.

Gate: the platform refuses with THEME_IMPORT_INVALID_TEMPLATE (theme upload, Theme Store publish and code save: the product and cart pages). Fields: product.price_notes, cart.price_notes, product.price_note, cart.price_note. Details: Money and languages

Numbers#

number.formatted#

The platform guarantees

The theme must

Counts and measures that need locale formatting come formatted.

Never format numbers in the theme.

Gate: none; the rule is not checked automatically. Details: Money and languages

Dates and times#

time.display#

The platform guarantees

The theme must

A time the shopper relies on comes ready to show beside its RFC 3339 value, in a field ending _display: store time with the store's timezone named, in the shopper's language, such as "Fri, Oct 30, 9:00 AM (Hong Kong Time)".

Print the _display value as given. Never format, convert or relabel these times in the theme; use the RFC 3339 field only for machines, such as <time datetime>.

Gate: none; the rule is not checked automatically. Advisory checks: themecheck time-display warns at upload, Theme Store publish and code save. Fields: cart.items[].gift_card_recipient.deliver_at_display, customer.gift_cards.items[].sent_at_display, customer.gift_cards.items[].scheduled_at_display. Details: Gift Cards in your theme

Countries and addresses#

address.form#

The platform guarantees

The theme must

customer.address_form gives the countries, per-country labels (city, state, zip), required fields, zip examples and, for countries whose addresses name one, the province options: subdivision codes labelled in the shopper's language. Addresses carry province (the code) and province_display (its name).

Build address forms only from address_form, and show the labels and required markers it gives.

Gate: none; the rule is not checked automatically. Advisory checks: themecheck bundled-countries warns at upload, Theme Store publish and code save. Fields: customer.address_form*. Details: Customer and forms

address.server#

The platform guarantees

The theme must

The server validates addresses (country, subdivision, postal code rules such as Indian pincodes) and refuses an invalid one with a translated message.

Don't add validation rules of your own that differ from the server's.

Gate: the platform refuses with STOREFRONT_CUST_COUNTRY_CODE_INVALID, SUBDIVISION_INVALID, VALIDATE_POSTAL_INVALID_FORMAT or VALIDATE_POSTAL_PINCODE_STATE_MISMATCH (customer addresses and checkout). Details: Customer accounts

Messages and labels#

messages.localized#

The platform guarantees

The theme must

Every result of a hosted action comes back as a localized message (form_error, form_notice, auth_notice, JSON user_errors) in the shopper's language.

Show the platform's message. Never write your own error or success copy for a platform action.

Gate: none; the rule is not checked automatically. Fields: form_error*, form_notice*, auth_notice*. Details: Response messages

labels.localized#

The platform guarantees

The theme must

Statuses, consent text, blocked reasons and filter labels come resolved for the shopper's language.

Show the given label. Never map codes to words of your own.

Gate: none; the rule is not checked automatically. Fields: *status_display, *.happened_at_display, *_label, *.status_name, *.display_value. Details: CTX overview

Store data#

ctx.needs#

The platform guarantees

The theme must

A section gets exactly the ctx roots its schema declares in ctx_needs.

Declare every root a section reads.

Gate: themecheck ctx-read refuses the theme at upload, Theme Store publish and code save. Details: Section schema

ctx.contract#

The platform guarantees

The theme must

ctx carries only the fields in the published contract.

Read only contract fields.

Gate: themecheck ctx-path refuses the theme at upload, Theme Store publish and code save. Fields: every ctx field. Details: CTX overview

metafields.declared#

The platform guarantees

The theme must

Installing a theme creates exactly the metafield definitions in config/metafields.json, adopting a matching definition the store already has.

Declare every metafield the theme's sections and blocks read.

Gate: themecheck metafield-undeclared refuses the theme at upload, Theme Store publish and code save. Fields: *.metafields*. Details: Metafields in themes

metafields.block#

The platform guarantees

The theme must

The platform metafield block (theme:metafield) shows any storefront-visible metafield of the page's item, a related item (a product's brand, vendor or category) or the store, with platform markup for every value type and the definition's name, translated per storefront language, as its label. Merchants add it in the theme editor or a connected AI tool; empty values render nothing.

The section that shows the page's own item (on a product, collection, category, vendor, brand, page or blog post template, the first section reading that item) must accept blocks (accept_app_blocks or accept_theme_blocks) and render them with {% blocks %}, so merchants can place the metafield block there. Style its tf-metafield classes.

Gate: none; the rule is not checked automatically. Advisory checks: themecheck metafield-block warns at upload, Theme Store publish and code save. Details: Metafields in themes

Settings#

settings.gate_pages#

The platform guarantees

The theme must

Required gate sections receive translated title and message settings; coming-soon notification outcomes and password errors are supplied as localized section settings.

Read settings.title and settings.message on coming_soon, password and maintenance; read settings.notify_state and show settings.notify_success, settings.notify_error or settings.password_error when supplied, and require explicit consent in /notify forms.

Gate: none; the rule is not checked automatically. Advisory checks: themecheck gate-text, notify-consent warn at upload, Theme Store publish and code save. Details: Gate pages

settings.ids#

The platform guarantees

The theme must

A product, collection, category, page, blog post or image setting stores the chosen item's id, and the template reads the item itself.

Store ids, never slugs or handles. A theme package's templates may name only items of its own demo catalogue, by their demo handle; installing the theme clears them.

Gate: themecheck setting-values refuses the theme at upload, Theme Store publish and code save; the platform refuses with THEME_SETTING_RESOURCE_ID_INVALID (theme editor save). Advisory checks: themecheck demo-picks warns at upload, Theme Store publish and code save. Details: Bound settings

settings.complete#

The platform guarantees

The theme must

The theme editor shows every setting a section, block or the theme declares with its label in the merchant's language (en, de, es, fr, it), the picker for its type, and its default until the merchant changes it.

Ship every new setting complete: a label key defined in every editor language file (locales/.schema.json for en, de, es, fr, it), a sensible default, and the type that fits the value: menu for a menu (not a text handle), color for a colour (not text), url for a link, image and video for media, the product, collection and other pickers for store items, datetime for a date.

Gate: themecheck setting-missing-label refuses the theme at upload, Theme Store publish and code save. Advisory checks: themecheck setting-type-mismatch warns at upload, Theme Store publish and code save. Details: Section schema

settings.no_orphans#

The platform guarantees

The theme must

A store's saved setting values (section and block settings on every page, their translations and theme settings) carry over every code save and theme update through the migrations the schemas declare.

When you rename, move or remove a setting, declare it in the schema's migrations so stored values move with it: rename_setting, move_setting or settings_to_blocks to keep a value, remove_setting to drop it on purpose. config/settings_schema.json takes rename_setting and remove_setting. A code save that would leave a store's saved value without its setting is refused, and so is a Theme Store version that drops a setting of the listing's latest version without a migration.

Gate: the platform refuses with THEME_CODE_ORPHANS_SETTING_VALUES (code save: the code editor, Store MCP save_theme_code, theme sync, a zip uploaded into a theme and Theme Store updates); the platform refuses with THEME_VERSION_ORPHANS_SETTINGS (Theme Store publish, against the listing's latest version). Details: Section schema

Images#

images.widths#

The platform guarantees

The theme must

When a store image is added, its 640 and 1080 px versions are prepared in the background as soon as it is stored. A bulk import prepares them only for the images listings show first: each product's first image and each category, brand and vendor image. Every other width, and every other image, is prepared on its first request, which waits for it once.

Use image_url: width: 640 for the src of product card images and image_url: width: 1080 for the product page's main image, with image_srcset for the other widths.

Gate: none; the rule is not checked automatically. Details: Platform filters

Customer account#

account.email_confirmation#

The platform guarantees

The theme must

customer.email_verified says whether the account email is confirmed. email_add with the customer's own unconfirmed email sends a code; email_add with the code confirms it.

When customer.email is set and email_verified is false, show a confirm prompt on the account page with a send-code control and a code field, using email_add only. Never use auth_code_send or login there: they are sign-in actions and always fail for a signed-in customer.

Gate: none; the rule is not checked automatically. Advisory checks: themecheck account-sign-in-forms warns at upload, Theme Store publish and code save. Fields: customer.email, customer.email_verified. Details: Customer accounts

account.forms_truthful#

The platform guarantees

The theme must

Every hosted account action accepts the multipart post the bridge sends and answers success only once the change is stored; otherwise it refuses with a translated message (such as STOREFRONT_CUST_INVALID_BIRTHDAY or STOREFRONT_CUST_MARKETING_NEEDS_EMAIL).

Show the platform's message. Offer the email marketing opt-in only when customer.email is set.

Gate: none; the rule is not checked automatically. Fields: customer.accepts_marketing. Details: Customer accounts

Gift Cards#

gift_card.recipient#

The platform guarantees

The theme must

When a Gift Card product is added to the cart or bought now, the platform checks the recipient: an email or a phone number in international format (+ and the country code, any country; stored as E.164), a name and a message within the lengths in forms.cart_add (the same for forms.buy_now) and a send time in the future. A recipient it cannot accept is refused with one message in the shopper's language and nothing is added. forms.cart_add.gift_card_recipient_email.available and forms.cart_add.gift_card_recipient_phone.available (the same in forms.buy_now) say whether the store can deliver a card by email and by text message now; a card the store cannot deliver is still refused at payment. With both an email and a phone, the card is sent by email. Without a recipient the card goes to the buyer's checkout contact. Cart lines carry the recipient's phone as phone_display and the send time as deliver_at_display.

On every form that adds a Gift Card product (product.is_gift_card), offer sending it as a gift with the recipient fields, showing the email and phone fields only while their available is true and leaving out the send-as-gift choice when neither is, take each field's max_length from forms.cart_add (or forms.buy_now) instead of writing numbers, and post the fields only while that choice is on. Send gift_card_deliver_at as RFC 3339 with the shopper's offset. Show the platform's message as given.

Gate: the platform refuses with GIFT_CARD_INVALID (cart_add and buy_now for a Gift Card product). Fields: product.is_gift_card, cart.items[].gift_card_recipient*, forms.cart_add*, forms.buy_now*. Details: Gift Cards in your theme

gift_card.message#

The platform guarantees

The theme must

The platform owns the Gift Card page's routes, lookups and refusals. After a balance check that fails for any reason, or on a view link that opens nothing, gift_card is null and form_error carries one message in the shopper's language that never says why.

Show the platform's message as given. Do not write your own not-found or reason text, and do not tell a wrong code apart from any other refusal.

Gate: none; the rule is not checked automatically. Fields: gift_card, form_error*. Details: Gift Cards in your theme

gift_card.code#

The platform guarantees

The theme must

gift_card.code holds the full code only on the card's view link; after a balance check it is null and gift_card.code_masked identifies the card. In customer.gift_cards only received cards carry a code, and it is null when the store no longer has it.

Print a code only where it is not null, and show code_masked otherwise. Never put a code in a URL, an analytics event, a cookie or storage.

Gate: none; the rule is not checked automatically. Fields: gift_card.code*, customer.gift_cards.items[].code*. Details: Gift Cards in your theme

Editing#

editing.merchant_editable#

The platform guarantees

The theme must

Merchants change their store's look and words in the theme editor, in their own language, without touching code, and keep those changes through theme updates.

Take visible copy, images, links, colours and layout choices from section, block or theme settings, or from locale keys through the t filter, never from literals in markup or section CSS. Text without letters (digits, punctuation, symbols such as × or →) is not copy; black or white with transparency for shadows and scrims is not a palette colour.

Gate: themecheck hardcoded-copy refuses the theme at upload, Theme Store publish and code save; themecheck hardcoded-asset-url refuses the theme at upload, Theme Store publish and code save; themecheck hardcoded-color refuses the theme at upload, Theme Store publish and code save. Details: Section schema

editing.prefer_settings#

The platform guarantees

The theme must

Template and setting values (the theme editor, templates/.json, config/settings_schema.json values) change a store's pages without a code change, and theme updates keep them.

When a change can be made through template or setting values, make it there instead of in code. Change code only for a new capability, and ship every new choice it brings as a setting, complete (settings.complete).

Gate: none; the rule is not checked automatically. Details: Section schema

live.edits#

The platform guarantees

The theme must

A connected tool or app never changes the live theme directly. Its writes to the live theme (code, theme settings, sections and blocks) are saved to its own pending copy of the live theme, checked and rendered there; shoppers see them only after the merchant reviews them in Store Admin or the mobile app and publishes them. Publishing applies them to the live theme as one change in its history, which can be undone.

Render correctly from a theme that is not live: every page works when previewed from a copy, and nothing depends on the theme's ID or on being the live theme.

Gate: none; the rule is not checked automatically. Details: Connector OAuth and MCP reference

Platform updates#

stability#

The platform guarantees

The theme must

A published theme keeps rendering the same way as the platform adds features: names and types stay, filters keep their arguments and output, and checks added later apply only to new versions.

Build only against behavior the docs describe, and check optional data before using it.

Gate: none; the rule is not checked automatically. Details: Theme stability