Skip to content

Layout, sections and blocks

The layout around every page, the sections merchants arrange, and the blocks inside them.

layout/theme.liqx is the HTML around every page. Four calls put the page together:

<template>
<!doctype html>
<html lang={shop.locale} dir={shop.direction}>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{product ? `${product.title} — ${shop.name}` : shop.name}</title>
{'base.css' | asset_url | stylesheet_tag}
</head>
<body class={`template-${template}`}>
{sections('header-group')}
{section('header')}
<main id="main">
{content_for_layout}
</main>
{section('footer')}
{appEmbeds()}
</body>
</html>
</template>
Call Prints
{content_for_layout} The page’s template: its sections, in order
{section('header')} One section, placed by the layout itself. The merchant edits its settings but can’t move or remove it
{sections('header-group')} A section group from sections/header-group.json: sections the merchant can add, reorder and remove in that area
{appEmbeds()} Snippets from installed apps that the merchant switched on, such as a custom code embed. Put it just before </body>

A section group file:

sections/header-group.json
{
"name": "Above the header",
"sections": {
"announcement": { "type": "announcement-bar", "settings": { "content": "Cash on delivery everywhere" } }
},
"order": ["announcement"]
}

A section is a file in sections/: a template plus a <schema> that declares its settings.

sections/featured-products.liqx
---
const { settings } = section;
---
<template>
<section class="featured">
<If condition={settings.heading !== ''}>
<h2>{settings.heading}</h2>
</If>
<div class="grid">
{products.slice(0, settings.limit).map(item => <ProductCard product={item} />)}
</div>
</section>
</template>
<schema>
{
"name": "Products",
"tag": "div",
"enabled_on": ["index"],
"settings": [
{ "type": "text", "id": "heading", "label": "Heading", "default": "Our products" },
{ "type": "range", "id": "limit", "label": "Products", "min": 4, "max": 20, "step": 1, "default": 8 }
],
"presets": [ { "name": "Products" } ]
}
</schema>

Inside a section, section.settings holds its values, section.id its id and section.blocks its blocks.

Key Means
name The name merchants see in the editor
settings The section’s settings. See Settings
blocks Which blocks it accepts (see below)
max_blocks The most blocks a merchant can add
presets Ready-made versions offered in “Add section”, each with a name, and optional settings and blocks
enabled_on The templates it may be added to, e.g. ["index", "product"]
enabled_in_groups The section groups it may be added to
tag The element Sworen wraps the section in (section by default). "none" means no wrapper. It must be an element name: null or "" is refused
class An extra class on that wrapper

Sworen adds the attributes the theme editor needs to the wrapper itself, so a merchant can click any section in the preview.

Blocks are the pieces a merchant adds inside a section: a question in a FAQ, a heading, an image. There are two kinds.

Declare them in the section’s own schema and loop over section.blocks. Put {block.lithos_attributes} on each block’s root element so the editor can select it:

<div class="faq">
{section.blocks.map(block => (
<details {block.lithos_attributes}>
<summary>{block.settings.question}</summary>
<div>{block.settings.answer}</div>
</details>
))}
</div>
"blocks": [
{
"type": "question",
"name": "Question",
"settings": [
{ "type": "text", "id": "question", "label": "Question" },
{ "type": "richtext", "id": "answer", "label": "Answer" }
]
}
]

A file in blocks/ is a block any section can accept. It has its own template and schema, and reads block.settings:

blocks/heading.liqx
---
const level = block.settings.level || 'h2';
---
<template>
<Switch value={level}>
<Match when="h1"><h1>{block.settings.text}</h1></Match>
<Default><h2>{block.settings.text}</h2></Default>
</Switch>
</template>
<schema>
{
"name": "Heading",
"settings": [
{ "type": "text", "id": "text", "label": "Text", "default": "Heading" },
{ "type": "select", "id": "level", "label": "Level", "default": "h2",
"options": [ { "value": "h1", "label": "H1" }, { "value": "h2", "label": "H2" } ] }
]
}
</schema>

A section (or a block) accepts theme blocks with { "type": "@theme" } for all of them, or by naming them, and prints them with {contentFor('blocks')}:

"blocks": [ { "type": "@theme" }, { "type": "_divider" } ]
<div class="page-content">{contentFor('blocks')}</div>
  • A block file whose name starts with _ (blocks/_divider.liqx) is private: @theme doesn’t include it, so a section has to name it.
  • Blocks can contain blocks: a block’s schema can accept blocks too, and prints them with contentFor('blocks').
  • Sworen wraps each block in a div (change it with tag). With "tag": "none", there is no wrapper and the editor attributes go on the block’s first element, which must then contain the whole block. "tag": null is refused.