3. Statements & the Label
A file is a stylesheet, then drawn statements in source order (SPEC 1), and
a container’s body nests the same idea: a { } style block, then a [ ] of children and
internal links.
The stylesheet
One { } block at the very top of the file — optional, omitted when there is
nothing to set up. Unlike an ordinary style block (declarations only), it is the
root’s setup block, so it additionally holds the file-global definitions:
| Item | Form | Means |
|---|---|---|
| Scene config | layout: grid; routing: orthogonal; | a declaration on the root — clearance / routing cascade to every link (SPEC 9) |
| Variable | --brand: #f60; | a themeable visual variable (colour / font) |
| Binding | my_r = 5; scale(n) = (…) | a compile-time value / function, bound with = — read in any expression (SPEC 10) |
| Rule | |box| { … } | style every box (an element selector) |
| Link rule | |-| { stroke: #666; } | style every link — the |-| selector (SPEC 9) |
| Descendant rule | |table| |box| { … } | style every box inside a table |
| Class | .hot { … } | define class hot |
| Id rule | #hero { … } | style the one node with id hero |
| Define | |treat::box| { … } | a new type treat, base box, with its defaults |
{
gap: 16; fill: --bg;
--brand: #ff6600;
scale(n) = (100 * 1.2^n);
|box| { radius: 6; }
|-| { stroke: #666; }
.hot { stroke-width: 2; }
|treat::box| { radius: 5; }
}
|treat::box| reads “treat is a box”; the :: sets a define apart from a
plain reference (|box|) at a glance. Defines chain (|panel::treat|) and may
carry intrinsic children (SPEC 9). Max inheritance depth 16; cycles are an
error.
Node declaration
|type#id| [ "label" ] [ .class… ] [ { style } ] [ [ children ] ]
Only the bars are required — and at least a type or an #id must sit inside
them. SPEC 1 names the parts; classes follow the bars
(|box| .hot, |box| .hot.loud), never sit inside them.
|cyl#db| "Postgres" .primary { fill: #eef } [
|badge| "v16"
]
| Form | Effect |
|---|---|
|box#cat| "" | same as |box#cat| — "" is just an empty string. |
|box| "Load balancer" | anonymous labelled box (can’t be linked to). |
|#cat| | a default |box|, id cat. |
The label
A node has no label unless you give it one — a bare |box#cat| is an empty box
(the #cat is a handle, like HTML’s id=, not text):
| Label | Means |
|---|---|
| no string at all | nothing — an empty box |
"X" | the label “X” |
"" | an empty string — nothing in flow, an empty cell in a grid (SPEC 12) |
A link to an undeclared name still draws a labelled box (Implicit nodes).
A multi-word label needs no [ ] (|box#lb| "Load balancer").
The label is smart — each type places it, and every layout extends the same rule (a chart’s label is its title, a series’ its legend entry — SPEC 14), so no type needs a hand-written caption or symbol. Give no label and a type places nothing:
"X" on | becomes |
|---|---|
|box| and the shapes (|oval|, |hex|, |cyl|, |diamond|, …) | its centred text |
|group| / |table| | its caption (SPEC 8) |
|icon| / |sign| | its symbol — |icon| "heart" is |icon| { symbol: heart } |
| a link | a label along the route (SPEC 9) |
a |chart| / series / |axis| / participant / frame | its title / legend / axis title / header / guard (SPEC 13, SPEC 14) |
The label takes no style of its own. The { } and classes after the head are
the node’s, so a styled, classed, or nudged label rides the [ ] content form
instead, where each string is a leaf in its own right (Text content):
|box#api| "API" .hot { fill: red } // label + class + the node's own style
|box#api| [ "API" { translate: 0 -6 } ] // a styled label, via content
The label and [ ] coexist — one inline label only (two or more strings go in
the [ ]). The label is the node’s one inline item, lowered by its type — a text
or caption child prepended to the [ ], or (for |icon|/|sign|) the symbol —
and the [ ] holds the rest:
|group#kitchen| "Kitchen" [ |box#bowl| "Bowl" ] // caption + a child
|icon| "bell" [ "3" ] // symbol + a text badge
Text content
A string is a text node — always a <text> leaf, never wrapped:
- In a box’s
[ ](or as the box’s label) it is that box’s text — centred when it is the only in-flow child, else a flow child laid out by the box’slayout. - On its own (on the canvas, or in a
[ ]) it is a free-standing flow / canvas text node. - Several strings are several text nodes —
"a" "b" "c"is three (a string is self-delimiting, so no;is needed between them). - An empty
""is suppressed (adds no text) — except as a grid cell, where it holds its track (SPEC 12). - Multi-line text uses
\n(or wraps atmax-width— SPEC 5); the box sizes to the widest line, with afont-size × 1.2leading between lines (plus anyline-spacing), lines aligned by the container’s packing knob (SPEC 6).
A string carries no children — text is a leaf, not a box — but where it is
content (free-standing, or a child in a [ ]) it takes the node tail: it
may wear classes and carry a style block of text properties —
"Starter" .card-title, "X" { color: red; font-weight: bold; translate: 0 -6; rotate: 12 }. In its own block only text-valid properties apply (color /
fill, every font-*, opacity, letter-spacing, line-spacing,
text-transform, text-decoration, text-shadow, translate, rotate,
layer); any other — pin, padding, width, a border, children, even
href / hint — needs a real box, so wrap the text in a |block|. A worn
class is looser, per the class law (SPEC 4):
its text-valid declarations land, the rest are inert on the text wearer. Set on
the string the style applies to it directly; set on a containing box it cascades
down (SPEC 6).
Implicit nodes
A link endpoint that is a single bare id not present in the link’s scope
auto-creates the node |box#cat| "cat" in that scope — a box named cat, labelled
“cat” — so cat -> dog -> bird is a complete three-box diagram. The same holds inside
a container body: a body link auto-creates its missing endpoints among that body’s own
children. Declaring the id in the scope — before or after the link — uses it instead
of creating one. A path endpoint (kitchen.bowl) is never auto-created: it must
resolve to an existing node, or it is an error. If a same-named node exists elsewhere
in the tree, the box is still created here and a warning names the other match.
An auto-created id that is a near-miss of a name already known in its scope —
a small typo (edit distance ≤ 2, and shorter than the id itself), or equal
ignoring case, against the declared and the previously auto-created names —
warns toward the likely target: cta -> bird warns did you mean 'cat'?
even in an all-implicit file. Distinct names stay silent — short ids (a -> b)
and numbered siblings (server -> server2) are families, not typos — so
legitimate mixed use draws no noise (SPEC 21). Auto-create is
box-only; the typed declare-at-first-use is the capsule endpoint
(cat -> |cyl#db| — SPEC 9).
Declarations
A declaration key: value; lives only in a { } style block — the stylesheet
(configuring the root) or a node’s own block — and ends with ;, so a value may
span lines (SPEC 2); the ; is optional only immediately before
}. A bare key: value outside a { } is an error. Every property, its value shape,
and where it applies is in the Property Ledger.