Grammar
section { } requires a name; omitting it is section block requires a name.
block { } does not require one.
A block’s TYPE is its file name, not the
name it declares.
blocks/social_row.slurp is the type social_row whatever its name: says. The
name is only a human label for the editor. A block file in a subdirectory
(blocks/a/b.slurp) is ignored at catalog-build time, because the renderer refuses
a / in a block type.The nine setting kinds
A value that fails its kind’s check is DROPPED and the default is kept. Nothing is
reported: validation happens at the merge boundary, in the host, not at compile
time.
Defaults
= default is optional. Without one, the kind’s default from the table above is
used. A default is written as a string, a bare identifier, a number, a negative
number, a boolean or null:
select default that is not one of its own options is an error:
select(...) options may be identifiers OR string literals, and so may the
default. select(left, center) and select("left", "center") parse identically.
Bare identifiers are not required.select() with no options is select needs at least one option.Meta
A trailing{ ... } object carries arbitrary keys. Three have meaning:
Colour values
color accepts a #hex of exactly 3, 4, 6 or 8 hex digits, or a CSS colour
keyword from a real allowlist (the named colours plus transparent,
currentcolor, inherit, initial). Anything else keeps the default, so a colour
setting can never carry ;, ( or url(...) into an attribute or a style context.
Link and image values
Both run through the same URL gate. A value with no scheme (a relative path, an anchor, a query string) is kept as written. A value WITH a scheme must behttp,
https, mailto or tel; anything else collapses to "".
image gets this gate too, validated where the value ENTERS rather than where a
theme uses it, because a theme is free to put it in background:url(...) where no
render-site check would fire.
Uniqueness and bounds at parse time
A file may carry both a
section { } and a block { }.
Nested blocks
A block definition may declare its ownblocks { } group, recursively. The nesting
is bounded by the parser’s depth limit of 64.
@theme targeting
Inside a blocks { } group, @theme says which THEME-LEVEL blocks (the ones in
blocks/*.slurp) may be inserted here.
Two resolution rules:
- A bare
@themealways wins over a whitelist, regardless of the order the two appear in. Widening is never silently narrowed. - An inline block definition always beats the shared theme catalog, so adding a block to the palette can never change an existing section’s behaviour.
Merge semantics
Saved editor state is merged over the schema at render time, TOLERANTLY, so a theme upgrade can never break stored state:
None of this is reported. The saved state itself is never modified; the merge only
decides what the render sees.
id and type on a section, and id on a block, pass a separate character gate
because they are editor-minted strings that templates put into attributes, CSS
selectors and JS strings. A value carrying a quote, an angle bracket, a backslash, a
backtick or whitespace is DROPPED rather than rewritten.
Hard ceilings
The 200 is a defensive ceiling, not an editor rule: a schema
max is the real
per-type cap, and its absence means “as many as the editor allows” up to this bound.
Reading a schema from a template
section is the merged object { id, type, settings, blocks }. Inside a block file
the same shape is bound to block instead, which is what lets {blocks} nest.