Block syntax
The complete directive syntax — fences, props, child blocks, and rows.
Blocks
Blocks use fenced directive syntax. A block opens with three (or more) colons plus a kebab-case name, and closes with a line containing only colons:
:::block-name{prop="value" count=3 enabled=true}
Block body
:::Rules:
- block names are lowercase kebab-case
- props are optional
- a block closes with
:::on its own trimmed line - nested blocks are allowed only when the parent block permits them
- unknown blocks produce diagnostics — they are never silently ignored
- block delimiters inside fenced code blocks (
```or~~~) are literal text - a closing fence matches the innermost open block; use a longer fence (
::::) on the outer block to disambiguate deep nesting
Props
Props use a constrained grammar — safe for generated content and portable across renderers:
{title="Quick Reference" count=3 variant="compact" featured=true}Supported values:
- quoted strings (
title="Hello \"world\"") - numbers (
count=3,ratio=-1.5) - booleans (
featured=true), with bare-key shorthand (featuredmeansfeatured=true) - bare identifiers for enums (
variant=compact)
Not supported, by design: JavaScript expressions, object literals, arrays, function references, JSX.
Child blocks
Child blocks use two-colon syntax and close implicitly at the next sibling or the parent close:
:::tabs
::tab{title="Fast"}
Use this when time matters.
::tab{title="Cheap"}
Use this when budget matters.
:::Three-colon blocks can nest inside child bodies; they must be balanced before the child ends.
:::tabs
::tab{title="Fast"}
Use this when time matters.
::tab{title="Cheap"}
Use this when budget matters.
:::Use this when time matters.
Pipe rows
Many blocks take rows of pipe-separated cells:
:::key-metrics
- 42% | Conversion lift
- 18ms | Median parse time
:::Each block defines its own columns and limits, so error messages name the exact
expectation. Escape a literal pipe inside a cell as \|.
:::key-metrics
- 42% | Conversion lift
- 18ms | Median parse time
:::When the rows don't match the block's column count, validation fails before any
render — and <Live> shows the same file:line:col diagnostic your build would:
:::key-metrics
- 42% | Conversion lift | extra column
:::example.md:2:1 error CB_ROW_COLUMNS
:::key-metrics rows require 2 columns (value | label). Found 3.
hint: Format: - value | label
example.md:1:1 error CB_ROW_COUNT
:::key-metrics needs at least 2 rows, found 0.Markdown bodies
Blocks like callout accept regular Markdown content:
:::callout{type="tip" title="Worth knowing"}
You can use **Markdown** inside this block.
:::The parser preserves the body verbatim — your app's Markdown pipeline renders it.
Raw HTML
Raw HTML is disabled by default everywhere. The static HTML renderer escapes all user content; if your project needs raw HTML, opt in at the renderer level and own sanitization.