Basker Docs

Theme settings

Define and access theme-level settings — global brand controls editors configure from the admin

Theme settings let editors configure global controls — brand colours, typography, social links, default behaviours — from the admin's theme configuration page. Defined once in config/settings_schema.json, accessed everywhere via the settings() global.

This page covers both defining and accessing settings. Template-level settings (per-page options) are covered in Templates.

Theme settings vs template settings

AspectTheme settingsTemplate settings
Defined inconfig/settings_schema.json{% schema %} inside a template
StructureGroups with a fields arrayA settings array
ScopeGlobal — applies across all pagesPer-page — applies only to pages using that template
Accessed viasettings() global functionpage.theme.settings on the document
Use caseBrand colours, fonts, social linksHero style, sidebar toggle, layout options
Edited in adminTheme configuration pagePage editor, per document

Use theme settings for values that should stay consistent site-wide. Use template settings for options that vary per page.

Defining theme settings

1. Create the settings schema file

your-theme/
├── blocks/
├── config/
│   └── settings_schema.json
├── layouts/
├── templates/
└── assets/

2. Define the groups array

The file is a JSON array of groups. Every group has a name.

The first group is reserved for theme metadata — your theme's name, version, author, and documentation links. It has no type and no fields:

[
  {
    "name": "theme_settings",
    "theme_name": "My Arts Centre",
    "theme_version": "1.0.0",
    "theme_author": "Your Studio",
    "theme_documentation_url": "https://example.com/docs",
    "theme_support_url": "https://example.com/support"
  }
]

Every other group is configurable. A configurable group sets "type": "group" and lists its controls in a fields array. Each field uses default for its starting value:

{
  "name": "global",
  "type": "group",
  "fields": [
    {
      "type": "text",
      "name": "site_title",
      "label": "Site title",
      "default": "My Arts Centre"
    }
  ]
}

3. Add a colour palette group

{
  "name": "colours",
  "type": "group",
  "fields": [
    {
      "type": "color",
      "name": "color_primary",
      "label": "Primary colour",
      "default": "#1a1a2e"
    },
    {
      "type": "color",
      "name": "color_secondary",
      "label": "Secondary colour",
      "default": "#e94560"
    },
    {
      "type": "color",
      "name": "color_background",
      "label": "Background colour",
      "default": "#ffffff"
    },
    {
      "type": "color",
      "name": "color_text",
      "label": "Body text colour",
      "default": "#333333"
    }
  ]
}

4. Add typography and social media groups

{
  "name": "typography",
  "type": "group",
  "fields": [
    {
      "type": "select",
      "name": "heading_font",
      "label": "Heading font family",
      "options": [
        { "label": "System default", "value": "system-ui" },
        { "label": "Georgia", "value": "Georgia, serif" },
        { "label": "Helvetica", "value": "Helvetica, Arial, sans-serif" }
      ],
      "default": "system-ui"
    },
    {
      "type": "select",
      "name": "body_font",
      "label": "Body font family",
      "options": [
        { "label": "System default", "value": "system-ui" },
        { "label": "Georgia", "value": "Georgia, serif" }
      ],
      "default": "system-ui"
    },
    {
      "type": "number",
      "name": "base_font_size",
      "label": "Base font size (px)",
      "default": 16
    }
  ]
}
{
  "name": "social",
  "type": "group",
  "fields": [
    { "type": "url", "name": "facebook_url", "label": "Facebook page URL" },
    { "type": "url", "name": "twitter_url", "label": "X (Twitter) profile URL" },
    { "type": "url", "name": "instagram_url", "label": "Instagram profile URL" },
    { "type": "url", "name": "youtube_url", "label": "YouTube channel URL" }
  ]
}

A group can also be repeatable — set "type": "array" with the same fields shape, and editors can add multiple entries (for example, a list of footer links).

All groups appear in the admin on the theme configuration page.

Accessing settings in templates

Theme-level: settings() global

The settings() function returns an object grouped the same way as the schema — each group's name is a key, and its fields sit beneath it:

{% assign theme_settings = settings() %}

<body style="
  --color-primary: {{ theme_settings.colours.color_primary }};
  --color-secondary: {{ theme_settings.colours.color_secondary }};
  --color-background: {{ theme_settings.colours.color_background }};
  --color-text: {{ theme_settings.colours.color_text }};
  --font-heading: {{ theme_settings.typography.heading_font }};
  --font-body: {{ theme_settings.typography.body_font }};
  --font-size-base: {{ theme_settings.typography.base_font_size }}px;
">

Or access individual settings inline through their group:

<a href="{{ settings().global.ticketLink }}">Find tickets</a>

Template-level: page.theme.settings

Template settings come from the document object — page.theme.settings for a page, event.theme.settings for an event, and so on. The variable matches the template's collection.

{% assign header_style = page.theme.settings.header_style %}
{% assign show_breadcrumbs = page.theme.settings.show_breadcrumbs %}

{% if show_breadcrumbs %}
  {% render 'breadcrumbs', page: page %}
{% endif %}

<header class="page__header page__header--{{ header_style }}">
  <h1>{{ page.title }}</h1>
</header>

Patterns

Inject brand colours as CSS custom properties

In layouts/default.liquid, settings flow into all templates as variables:

<head>
  <meta charset="utf-8">
  <title>{{ page.title | default: settings().global.site_title }}</title>
  {{ content_for_header }}

  {{ "theme.css" | asset_url | stylesheet_tag }}

  <style>
    :root {
      --color-primary: {{ settings().colours.color_primary | default: '#1a1a2e' }};
      --color-secondary: {{ settings().colours.color_secondary | default: '#e94560' }};
      --color-bg: {{ settings().colours.color_background | default: '#ffffff' }};
      --color-text: {{ settings().colours.color_text | default: '#333333' }};
      --font-heading: {{ settings().typography.heading_font | default: 'system-ui' }};
      --font-body: {{ settings().typography.body_font | default: 'system-ui' }};
    }
  </style>
</head>

Per-page hero variant

{% assign hero_variant = page.theme.settings.hero_variant %}
{% assign overlay_opacity = page.theme.settings.overlay_opacity %}

{% if page.image %}
  <section class="hero hero--{{ hero_variant | default: 'full-width' }}">
    <img
      src="{{ page.image | image_url: width: 1600, height: 900, fit: 'cover' }}"
      alt="{{ page.image.alt | default: page.title }}"
      loading="eager"
    >
    {% if hero_variant == 'overlay' %}
      <div class="hero__overlay" style="opacity: {{ overlay_opacity | default: 0.5 }};">
        <h1 class="hero__title">{{ page.title }}</h1>
      </div>
    {% endif %}
  </section>
{% endif %}

<div class="page__body">
  {% stageblocks page %}
</div>

{% schema %}
{
  "settings": [
    {
      "type": "select",
      "name": "hero_variant",
      "label": "Hero style",
      "options": [
        { "label": "Full width", "value": "full-width" },
        { "label": "Overlay", "value": "overlay" },
        { "label": "Side by side", "value": "side-by-side" }
      ],
      "defaultValue": "full-width"
    },
    {
      "type": "number",
      "name": "overlay_opacity",
      "label": "Overlay opacity (0 to 1)",
      "defaultValue": 0.5
    }
  ],
  "blocks": ["text-section", "image-gallery", "call-to-action"]
}
{% endschema %}

Conditional rendering based on settings

{% assign social = settings().social %}

{% if social.facebook_url != blank or social.twitter_url != blank or social.instagram_url != blank %}
  <nav class="social-links" aria-label="Social media">
    <ul>
      {% if social.facebook_url != blank %}
        <li><a href="{{ social.facebook_url }}" rel="noopener">Facebook</a></li>
      {% endif %}
      {% if social.twitter_url != blank %}
        <li><a href="{{ social.twitter_url }}" rel="noopener">X (Twitter)</a></li>
      {% endif %}
      {% if social.instagram_url != blank %}
        <li><a href="{{ social.instagram_url }}" rel="noopener">Instagram</a></li>
      {% endif %}
      {% if social.youtube_url != blank %}
        <li><a href="{{ social.youtube_url }}" rel="noopener">YouTube</a></li>
      {% endif %}
    </ul>
  </nav>
{% endif %}

Troubleshooting

"Invalid settings_schema.json" — JSON syntax error. Validate with a JSON linter.

"settings_schema.json should be an array of objects" — The root must be [], not {}.

"settings_schema.json entries should include a name property" — Every group needs a name.

Settings return empty values — The setting name in the schema must match the property name used in the template. Names are case-sensitive.

settings() returns an empty object — The theme has no config/settings_schema.json, or the file failed validation. Check the upload validation log.

Template settings not available on page.theme.settings — The template needs a valid {% schema %} with a settings array. Validate the schema JSON.

On this page