Site settings

Values used across the whole site — social links, a contact email, an announcement, a font choice — are site settings. The theme defines them, editors change them in the admin, and every template reads them.

Defining settings

Define them in theme/config/site_settings.json, in the same JSON Schema form a section’s {% schema %} uses — the same field types, title, description, default and group:

JSON
{
  "title": "Site settings",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "announcement_text": {
      "type": "string",
      "title": "Announcement bar text",
      "description": "Shown above the header on every page. Leave empty to hide.",
      "default": "",
      "group": "Announcement bar"
    },
    "announcement_link": {
      "type": "string",
      "format": "url",
      "title": "Announcement bar link",
      "default": "",
      "group": "Announcement bar"
    },
    "instagram_url": {
      "type": "string",
      "format": "url",
      "title": "Instagram link",
      "default": "",
      "group": "Social links"
    },
    "headline_font": {
      "type": "string",
      "title": "Headline font",
      "enum": ["Serif", "Sans-serif"],
      "default": "Serif",
      "group": "Typography"
    }
  }
}
Not settings_schema.json

Shopify uses config/settings_schema.json for its own, different format, and Shopify’s VS Code extension marks a Minamo file of that name as wrong — which can lead to it being “fixed” into Shopify’s form, where Minamo can’t read it. Sites made with 0.7.0 or 0.7.1 used that name: it still works in Minamo’s form, with a warning to rename it to site_settings.json.

Using settings in templates

Every layout, section, block and snippet reads them as {{ settings.<name> }} — including snippets pulled in with {% render %}, with no need to pass them along. (section.settings and block.settings are different: those belong to one section or block.)

Liquid
{% if settings.announcement_text != blank %}
  <div class="announcement">
    {% if settings.announcement_link != blank %}
      <a href="{{ settings.announcement_link }}">
        {{ settings.announcement_text }}
      </a>
    {% else %}
      <p>{{ settings.announcement_text }}</p>
    {% endif %}
  </div>
{% endif %}
  • Defaults fill the gaps. A setting nobody has saved yet has its default, or is blank without one. A property in required must have a valid default, as for sections.
  • Check a link isn’t blank before rendering it, as above.
  • Fonts: offer the choices with enum and map each to a font stack in the layout, so the site only ever uses fonts it was designed for — the theme loads the font files itself:
    Liquid
    {% if settings.headline_font == 'Sans-serif' %}
      <style>:root { --headline: var(--sans); }</style>
    {% endif %}

Grouping settings

Settings with the same "group" appear under one collapsible heading on the admin’s Settings screen. Group by part of the site: Header, Announcement bar, Footer, Social links, Contact, Typography. See Grouping fields for the general advice.

Where the values live

The values editors save are content, in content/settings.json:

JSON
{
  "schemaVersion": 7,
  "settings": {
    "instagram_url": "https://instagram.com/example",
    "headline_font": "Sans-serif"
  }
}
  • No drafts. Like menus, a save is committed and live at once, and recorded in the site’s history. The admin’s preview shows unsaved changes as they’re made, before they’re saved.
  • Pull and push carry them with the rest of the content, protected the same way as pages: settings changed on the live site since your last pull stop a push, and a copy with no settings.json never clears the live ones. See Pull, push & upgrading.
  • No file, no settings. A theme without config/site_settings.json simply has none. A file that can’t be used (not valid JSON, not a JSON Schema object, or a required property with no valid default) is left out with a start-up warning, like a broken section.