Theme directory structure
Required and optional directories, file naming conventions, and asset size limits for Basker themes
Themes are packaged as ZIP files and processed during upload. The directory structure decides how blocks, templates, layouts, settings, and assets are extracted into the theme manifest.
Overview
| Directory | Required | Purpose |
|---|---|---|
blocks/ | Yes | Reusable content blocks with schema definitions |
layouts/ | Yes | HTML shell templates that wrap page content |
templates/ | Yes | Collection-specific page templates |
assets/ | No | Static files — CSS, JavaScript, images, fonts |
snippets/ | No | Reusable Liquid partials included via {% render %} |
components/ | No | Larger Liquid partials for page-level UI like headers, footers, or card components, also resolved by {% render %} |
config/ | No | Theme-level configuration files |
.well-known/ | No | Files served at /.well-known/* for domain verification, security disclosure, and similar use cases |
Required directories
blocks/
.liquid files defining reusable content blocks. Each must include a {% schema %} tag with a JSON object containing at minimum name (string) and settings (array).
Blocks are available to editors in the admin. The schema defines the fields editors fill in when adding a block to a page.
blocks/
hero-banner.liquid
text-section.liquid
image-gallery.liquid
call-to-action.liquidSchema requirements:
| Property | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique identifier across all blocks in the theme |
settings | array | Yes | Field definitions for editor inputs |
label | string | No | Display name in the editor; defaults to name |
singular | string | No | Singular label; defaults to name |
plural | string | No | Plural label; defaults to name |
Example block file:
<section class="hero-banner">
<h1>{{ block.heading }}</h1>
<p>{{ block.subheading }}</p>
</section>
{% schema %}
{
"name": "hero_banner",
"label": "Hero Banner",
"singular": "Hero Banner",
"plural": "Hero Banners",
"settings": [
{
"name": "heading",
"label": "Heading",
"type": "text"
},
{
"name": "subheading",
"label": "Subheading",
"type": "textArea"
}
]
}
{% endschema %}Validation rules:
- Missing
{% schema %}produces an error during upload validation. The local theme checker reports it as a warning so you can keep iterating. - Missing
nameproduces an error. settingsmust be an array; a non-array value produces an error.- Duplicate
namevalues across block files produce a warning. - Multiple
{% schema %}tags in one file: only the first is used; a warning is issued.
See Block schema reference for the full field type list.
layouts/
.liquid files defining the outer HTML shell. Every theme must include layouts/default.liquid.
layouts/
default.liquid
checkout.liquid
blank.liquidRequired placeholders:
| Placeholder | Purpose | Placement |
|---|---|---|
{{ content_for_header }} | Basker injects scripts, meta tags, JSON-LD, and preview banners | Inside <head> |
{{ content_for_layout }} | Basker outputs the rendered template body captured by the template | Inside <body> |
Templates can reference a specific layout using {% layout %}. If no layout is specified, default.liquid is used.
Validation rules:
- Missing
layouts/default.liquidproduces an error. - A
{% layout %}referencing a non-existent layout file produces an error.
templates/
.liquid files controlling how content is rendered for specific collections. Filenames follow a strict naming convention.
templates/
page.liquid
page.landing.liquid
event.default.liquid
post.default.liquid
blog.default.liquid
index.liquidFile naming convention:
[collection].liquid or [collection].[name].liquid| Segment | Description | Examples |
|---|---|---|
collection | The Basker content collection this template serves | page, event, post, blog, organization, person, venue, work, season, series |
name | The template variant identifier | default, landing, sidebar, full-width |
A template with only one segment before .liquid (like index.liquid or page.liquid) is treated as a base template where collection is the filename and name defaults to "default".
Schema structure (optional but recommended):
{% schema %}
{
"settings": [
{
"name": "show_sidebar",
"label": "Show sidebar",
"type": "switch"
}
],
"blocks": ["hero_banner", "text_section"]
}
{% endschema %}| Property | Type | Required | Description |
|---|---|---|---|
settings | array | No | Template-level field definitions |
blocks | array | No | Names of blocks permitted on pages using this template |
blocks array entries must match name values defined in blocks/. A reference to a non-existent block name produces a warning.
Validation rules:
- Missing
{% schema %}produces a warning. - No
index.liquidorpage.liquidordefault.liquidproduces an error. - Invalid JSON in
{% schema %}produces an error. settingsas a non-array value produces a warning.blocksentries referencing undefined block names produce a warning.
Optional directories
assets/
Static files served via the Basker CDN. Reference assets in Liquid using asset_url.
assets/
css/
main.css
typography.css
js/
navigation.js
images/
logo.svg
hero-bg.jpg
fonts/
custom-font.woff2Referencing assets:
<link rel="stylesheet" href="{{ 'css/main.css' | asset_url }}">
<script src="{{ 'js/navigation.js' | asset_url }}"></script>
<img src="{{ 'images/logo.svg' | asset_url }}" alt="Logo">Allowed file extensions:
| Category | Extensions |
|---|---|
| Stylesheets | .css |
| Scripts | .js |
| Data | .json |
| Images | .gif, .ico, .jpg, .jpeg, .png, .svg, .webp |
| Fonts | .eot, .ttf, .woff, .woff2 |
Files with extensions outside this list are rejected during validation with an error.
Size limits:
| File type | Maximum size |
|---|---|
| Images (gif, ico, jpg, jpeg, png, svg, webp) | 1.5 MB |
| All other assets | 2.5 MB |
Exceeding either limit produces a warning.
Validation rules:
- Disallowed file extensions produce an error.
- Assets referenced in Liquid but missing from
assets/produce an error. - Assets present in
assets/but not referenced in any Liquid file produce a warning (orphaned assets).
snippets/
Reusable Liquid partials, included in layouts/templates/blocks via {% render %}.
snippets/
social-links.liquid
breadcrumbs.liquid
pagination.liquidUsage:
{% render 'social-links' %}
{% render 'pagination', total_pages: paginate.pages, current_page: paginate.current_page %}The {% include %} tag is deprecated. Use {% render %} instead. Files using {% include %} produce a deprecation warning during validation.
Validation rules:
- A
{% render %}reference to a snippet that doesn't exist insnippets/,components/, orblocks/produces a warning. - Snippets present in
snippets/but never referenced produce a warning (unused snippets).
components/
A second home for Liquid partials, conventionally used for larger page-level UI — global headers, footers, navigation menus, card components — while snippets/ holds smaller reusable bits like icons or formatters.
components/
global-header.liquid
global-footer.liquid
event-card.liquid{% render %} resolves partials against snippets/, components/, and blocks/, so a call like {% render 'components/global-header' %} works without extra configuration. The split is a convention only; the validator treats both directories the same way.
config/
Theme-level configuration files.
config/
settings_schema.jsonsettings_schema.json
Defines theme-wide settings that appear in the admin under the theme's settings panel. The file must be a JSON array of setting groups.
[
{
"name": "theme_settings",
"settings": [
{
"name": "primary_colour",
"label": "Primary colour",
"type": "color"
},
{
"name": "font_family",
"label": "Font family",
"type": "select",
"options": [
{ "label": "Sans-serif", "value": "sans-serif" },
{ "label": "Serif", "value": "serif" }
]
}
]
}
]Each entry is a setting group. Each must have a name property.
Validation rules:
- File must parse as valid JSON.
- Top-level value must be an array.
- Non-object entries produce a warning.
- Entries missing
nameproduce a warning.
Tenant locking: A setting group with "name": "theme_settings" may include a "tenant" property. Setting it locks the theme to that tenant's slug — uploading the theme to any other site produces an error.
.well-known/
Files in .well-known/ are served verbatim at /.well-known/<filename> on the live site. Common uses include apple-app-site-association, assetlinks.json, and security.txt.
.well-known/
apple-app-site-association
assetlinks.json
security.txt.well-known/ files are validated more strictly than regular assets.
Allowed extensions:
| Extension | Notes |
|---|---|
.json | JSON manifest files |
.pem | PEM-encoded certificates |
.txt | Plain text |
.xml | XML documents |
| (no extension) | Permitted — many .well-known standards use extensionless filenames |
Size limit: 100 KB per file.
Validation rules:
- Disallowed extensions produce an error.
- Files larger than 100 KB produce a warning.
- Path segments must be simple and traversal-safe; nested subdirectories under
.well-known/are not allowed.
Ignored directories
| Directory | Reason |
|---|---|
__MACOSX/ | Skipped when the theme ZIP is unpacked |
examples/ | Excluded from manifest generation |
Allowed field types
Field types used in settings arrays across blocks, templates, and settings_schema.json.
| Type | Description |
|---|---|
text | Single-line text input |
textArea | Multi-line text input |
richText | Rich text editor with formatting |
number | Numeric input |
checkbox | Boolean checkbox |
switch | Boolean toggle switch |
select | Dropdown selection |
radio | Radio button group |
color | Colour picker |
date | Date picker |
dateTime | Date and time picker |
email | Email address input |
url | URL input |
upload | File or image upload |
relationship | Reference to another document |
code | Code editor |
json | JSON editor |
hidden | Hidden field — not shown to editors |
group | Groups nested fields together |
array | Repeatable set of fields |
Type aliases: richtext is normalised to richText, textarea to textArea, datetime to dateTime. Unsupported type values produce a warning.
Nesting: group and array types support a fields sub-array for nested field definitions. Nesting deeper than four levels produces a best-practice warning.
Complete example
my-theme/
assets/
css/
main.css
variables.css
js/
navigation.js
gallery.js
images/
logo.svg
default-hero.jpg
fonts/
brand-regular.woff2
brand-bold.woff2
blocks/
hero-banner.liquid
text-section.liquid
image-gallery.liquid
call-to-action.liquid
feature-grid.liquid
config/
settings_schema.json
layouts/
default.liquid
checkout.liquid
snippets/
social-links.liquid
breadcrumbs.liquid
pagination.liquid
seo-meta.liquid
templates/
index.liquid
page.liquid
page.landing.liquid
event.default.liquid
post.default.liquid
blog.default.liquidRelated
- Tutorial — apply this structure to a working theme.
- The theme manifest — how directory contents are transformed during upload.