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.
| 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 | Must match the block's filename without the .liquid extension |
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. - Put exactly one
{% schema %}tag in each block. Multiple schema tags produce a validation warning and do not have a stable merge order.
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> |
Every template must declare its layout explicitly with {% layout 'layouts/default.liquid' %}. There is no implicit default: a template without a {% layout %} tag renders on its own, and since template bodies are wrapped in {% capture content_for_layout %}, an omitted layout tag produces an empty page.
Validation rules:
- Missing
layouts/default.liquidproduces a validation error. It is one of the structural errors that prevents upload. - A
{% layout %}referencing a non-existent layout file produces a validation error, but is reported as a theme-health issue rather than a structural upload blocker.
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.liquid,page.liquid, ordefault.liquidproduces a structural error that prevents upload. - 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 and metadata | .json, .map, .txt, .xml, .webmanifest |
| Images | .gif, .ico, .jpg, .jpeg, .png, .svg, .webp |
| Fonts | .eot, .ttf, .woff, .woff2 |
| Other supported source | .vue |
Files with extensions outside this list produce a validation error. This is reported in theme health, but it is not one of the structural errors that prevents upload.
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: total_pages, current_page: current_page %}The {% include %} tag is deprecated. Use {% render %} instead. Files using {% include %} produce a deprecation warning during validation.
Validation rules:
- A
{% render %}reference is resolved againstsnippets/,components/, andblocks/for a bare name, or from the theme root when the reference contains a path separator. Anything that resolves to neither 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.liquidcomponents/ is a naming convention rather than a separate Liquid root. Render a component with its path from the theme root, such as {% render 'components/global-header' %}. Files in snippets/ can be rendered without the directory prefix, such as {% render 'social-links' %}.
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.
- Keep files directly inside
.well-known/so their public URL is predictable. Paths are normalised and checked before files are served.
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.