Skip to main content

Theme Manifest & Settings Configuration (theme.json)

The theme.json file is the mandatory manifest at the root of every SitePack theme. It registers your theme's identity with the platform and declares two kinds of merchant-facing customization:

  1. Customizer Settings — simple color/dimension/option inputs that appear in the SitePack Theme Editor and control CSS custom properties in your theme.
  2. Custom Templates — structured, headless-style content templates (like a landing page) that merchants can fill in without touching code.

:::info Format Changed If you've seen older sample themes with a separate config/settings_schema.json file and settings grouped into named categories ({ "name": "Colours", "settings": [{ "id": ..., "label": ... }] }) — that format is retired. The current schema (below) lives entirely inside theme.json and uses a flat settings array with no category wrapper and no id field. label is still read, though — supply it so the Theme Editor shows a readable name instead of a humanised key. Building a new theme from the Official Theme Skeleton already gives you the current format — this page describes what's actually in that skeleton. :::


The Root Manifest Structure

Here is the complete, real theme.json from the Official Theme Skeleton:

{
"uuid": "sitepack-theme-skeleton",
"name": "Base Skeleton",
"author": "SitePack Team",
"version": "1",
"supports_site": true,
"supports_online_store": true,
"settings": [
{
"type": "colour",
"key": "header-background-color",
"label": "Header background",
"default": "#ffffff"
},
{
"type": "colour",
"key": "footer-background-color",
"default": "#3f434a"
}
],
"templates": []
}

Root Metadata Fields

  • uuid (string, required): The theme's unique identifier. When you run sitepack theme:init, the CLI requests a fresh UUID from the SitePack API and writes it here automatically — you don't need to invent one by hand.
  • name (string, required): The display name shown in the Theme Editor and (if published) the Theme Marketplace.
  • author (string, required): The developer or agency name. sitepack theme:init fills this in from your logged-in profile automatically.
  • version (integer, required): A plain incrementing integer ("1", "2", "3"...) — not Semantic Versioning. You don't need to bump this yourself: every time you run sitepack theme:publish, SitePack assigns and returns the next version number automatically, and the CLI prints it to your terminal (✅ Theme published successfully! New version: 4). Treat this field as read-only/informational in your local theme.json.
  • supports_site (boolean, required): Set true if the theme can be used for standard (non-commerce) websites.
  • supports_online_store (boolean, required): Set true if the theme supports e-commerce storefront features (product, category, cart templates).
  • settings (array, optional): Your customizer settings — see below. Defaults to an empty array if omitted.
  • templates (array, optional): Your custom page templates — see below. Provide [] if you don't need any.

Defining Customizer Settings ("settings")

The "settings" array is a flat list — there is no grouping/category wrapper. Each entry becomes one input in the SitePack Theme Editor, and its resolved value becomes available in your Twig templates and as a --key-named CSS custom property.

Setting Object Fields

  • key (string, required): The unique identifier for this setting. Used to look it up in Twig and to name its CSS variable.
  • type (string, required): The input type. Supported values:
    • colour — a color picker, returning a hex string (note the British spelling; color is accepted as an alias but colour is canonical).
    • pixels — a numeric input for pixel-based dimensions (e.g., border radius, container width).
    • font — a font picker. The value is a font key with underscores (Open_Sans).
    • options — a dropdown/select input for a small set of predefined choices. Add an options array alongside it.
    • text, textarea, image, list — content-shaped settings. These are not meant to drive CSS.
  • default (required): The fallback value used until a merchant changes it.
  • label (string, optional but recommended): The text shown next to the input in the Theme Editor. Without it the Admin falls back to a humanised version of the key (header-background-color → "Header background color").

:::caution pixels defaults must carry their unit Write "default": "8px", not "default": 8. A setting's default is emitted into :root verbatim, so a bare number produces the invalid declaration --card-border-radius: 8. (The unit is only appended automatically when a merchant saves a value in the Admin — which means a bare-number default looks fine the moment anyone touches the setting, and broken for everyone who does not.) :::

Only colour, pixels and font settings are intended to resolve to CSS custom properties. Every declared key is emitted as a --key variable regardless of type, so a text or image setting will also appear in :root — just not usefully.

Complete Example

{
"uuid": "your-theme-uuid",
"name": "Summit Minimalist",
"author": "CodeBrothers",
"version": "1",
"supports_site": true,
"supports_online_store": true,
"settings": [
{ "type": "colour", "key": "text-color", "label": "Body text colour", "default": "#333333" },
{ "type": "colour", "key": "background-color", "label": "Page background", "default": "#ffffff" },
{ "type": "colour", "key": "header-background-color", "label": "Header background", "default": "#ffffff" },
{ "type": "colour", "key": "header-text-color", "label": "Header text", "default": "#333333" },
{ "type": "pixels", "key": "card-border-radius", "label": "Card corner radius", "default": "8px" }
],
"templates": []
}

Reading Settings Values

You do not need to read settings in Twig at all. SitePack already emits a :root block containing a --key custom property for every setting you declare — merchant value where one is set, your default otherwise. It arrives via the stylesheet that sitepack_head() links, so all your CSS has to do is consume it:

/* assets/css/theme.css */
header {
background: var(--header-background-color);
}

That is the whole mechanism. Change the setting in the Admin, the variable changes, the site follows.

:::danger Do not emit your own :root block A <style> block that writes --header-background-color: … in your layout is rendered after the platform stylesheet, so it overrides the merchant's real choice with whatever value your template computed. The site then ignores the Theme Editor entirely. :::

Settings whose type is not colour, pixels or font (i.e. text, textarea, image, list) are content rather than styling. Read those on a custom template through the page object — see Custom Templates Manifest Configuration below.

:::tip You Get Brand Colors for Free You do not need to declare settings for basic brand colors, links, fonts, border radius, or container width — SitePack always exposes the merchant's core Admin → Design settings as ready-to-use CSS variables (--main-color, --link-color, --link-active-color, --link-text-decoration, --border-radius, --container-width, --heading-font, --body-font, and more), automatically injected by sitepack_head(). Reserve your own theme.json settings for things specific to your design that aren't already covered — like per-section background colors. See the Twig Objects Guide for the full list. :::


Headless-Style Custom Layout Templates

Beyond simple settings, theme.json lets you declare custom templates: a Twig file you design once, paired with a set of structured content fields a merchant fills in through the Admin — no drag-and-drop page builder, no broken layouts.

:::note Don't recreate core content as template fields Template fields are for content that is specific to one page (a hero title, a landing-page intro). Content that is reused across the site and managed centrally — the newsletter, USPs, the topbar and the agenda — comes from core through dedicated functions, not from theme.json fields. Don't add fields like usp-1-title, agenda-items or newsletter texts to your manifest; use sitepack_usps(), sitepack_events(), sitepack_topbar() and sitepack_newsletter_form() instead. See Content that comes from core. :::

Custom Templates Manifest Configuration

Append a "templates" array to your theme.json:

{
"templates": [
{
"key": "landing-page",
"name": "Landing page",
"fields": [
{ "key": "hero-title", "type": "text", "label": "The main hero title" },
{ "key": "hero-sub-head", "type": "text", "label": "The main hero sub heading" },
{ "key": "hero-sub-paragraph", "type": "textarea", "label": "The main hero sub paragraph" },
{ "key": "hero-image", "type": "image", "label": "The main hero image" },
{ "key": "hero-cta-label", "type": "text", "label": "The main hero CTA label" },
{ "key": "hero-cta-url", "type": "text", "label": "The main hero CTA URL" },
{ "key": "usps", "type": "list", "label": "USPs" }
]
}
]
}

Template Registration Properties

  • key (string, required): Unique identifier for the template. Must match the Twig filename: templates/{key}.twig (e.g., templates/landing-page.twig).
  • name (string, required): The human-friendly label shown in the template picker in the SitePack Admin.
  • fields (array, required): The content fields merchants fill in for pages using this template.

Field Entry Schema

  • key (string, required): Lookup identifier for the field — used as a bracket-notation key in Twig.
  • type (string, required): One of text, textarea, image, or list.
  • label (string, required): Descriptive text shown next to the field in the Admin editor.
  • translatable (boolean, optional): Forces whether the field's value is offered for translation when the merchant buys an extra language. Leave it out and SitePack decides — see below.

Which Fields Get Translated

A page built on your template renders from these field values, not from the page builder, so they are the copy a visitor actually reads. When the merchant buys an extra language, SitePack offers them for translation automatically:

  • text, textarea and list fields are offered — each list item separately, labelled with the label you gave the field.
  • image fields are not: an asset is not language.
  • Within the offered types, a value that is plainly not a sentence is skipped as well — a URL or path, a price or a bare number, an icon class, a true/false flag. That keeps hero-cta-url out of the translation editor even though it is typed text.

Set translatable explicitly when one of your fields is the exception:

{
"fields": [
{ "key": "hero-cta-url", "type": "text", "label": "CTA URL", "translatable": true },
{ "key": "tracking-label", "type": "text", "label": "Tracking label", "translatable": false }
]
}

A field the merchant has not translated falls back to the master value, so a half-translated language still renders a complete page.

Name your fields for what they hold

The automatic rule reads the last segment of the key too: …-url, -link, -slug, -icon, -class, -param, -id, -email and -phone are treated as machine values. Naming a link field hero-cta-url rather than hero-cta-2 gets you the right behaviour for free.

See Theme Translations for what the merchant sees.

Accessing Custom Template Fields in Twig

Field values are exposed on the page object, keyed by the field's key. Because keys commonly contain hyphens, always use bracket notation:

{% extends 'layouts/base.twig' %}

{% block content %}
{% if page['hero-title'] %}
<h1 class="landing-hero__title">{{ page['hero-title'] }}</h1>
{% endif %}

{% if page['hero-cta-label'] and page['hero-cta-url'] %}
<a href="{{ page['hero-cta-url'] | ensure_link }}" class="btn-primary btn-lg">
{{ page['hero-cta-label'] }}
</a>
{% endif %}

{% if page['usps'] %}
<ul class="landing-usps">
{% for usp in page['usps'] %}
<li>{{ usp.label | default(usp) }}</li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}

Exploring Further:

Now that you have configured your manifest and inputs, understand how static assets are served via the CDN in the Theme Assets Guide!