2. Lexical Syntax

PropertyValue
Extension.lini
EncodingUTF-8 (BOM ignored)
Line endingsLF or CRLF (normalized on read)
Comments// … to end of line. No block comments.
Statement endA node/link/text statement ends at a newline or ;. A declaration ends at ; — its value runs to that ; (or a closing }), so a value may span lines.
Identifier[a-zA-Z_][a-zA-Z0-9_-]* — case-sensitive, ASCII, dash-case

Whitespace is insignificant except as a token separator and where a rule below says otherwise:

FormWhitespace rule
|…|Identity in bars: a type, an optional #id (|box#cat|), or an id alone (|#cat|). :: is the define operator (|cat::oval|). Bars are paired; surrounding space at the boundary is not allowed.
#idInside the bars it names the node’s id; at a rule’s head it is an id selector (#cat { }). A # followed by hex digits in a value is a colour (#f80); the two never meet — one heads a statement or sits in bars, the other is a value.
key: value: separates name and value; surrounding space optional, canonical is one space after (radius: 5).
a:sideA : after a link endpoint forces a side (a:left). Distinct from the declaration : by position — it follows an endpoint, never opens a value.
.name (class)At a rule head it is a class selector / definition (.hot { … }). On an instance, link, or text leaf it is a worn class, following the identity — spaced off it (|box| .hot, a -> b .loud, "x" .quiet), the rest of the chain glued (.hot.loud).
id.childNo space — an endpoint path into a child (kitchen.bowl).
--nameA variable, in a value or at a statement start to declare one.
link op[marker?] line [marker?], glued, no internal space (->, --->, <->).
[ … ]A content list. Paired; whitespace inside is insignificant.

Strings — double-quoted UTF-8: "…". Escapes: \", \\, \n, \t. A double-quoted string is always text; leading and trailing whitespace in its value is trimmed (" ABC " is “ABC”, and a spaces-only " " becomes ""), so source spacing never leaks into the render. Single quotes are not strings (reserved, SPEC 23).

A bare word is an identifier, never a string. In a value, an unquoted word is always an identifier — a keyword, a colour or symbol name, a font-family, or an id reference — so literal text is always quoted: a string-valued property (title, hint, href, src, path) takes a "…" even with no spaces. The one hybrid is a name that may contain spaces — font-family — bare or quoted, quoted only when needed (font-family: "SF Mono"), as in CSS. Numbers and (…) expressions are bare too; only text is quoted.

Expressions — a parenthesized region (…) is a compile-time math expression, folded to a literal number (or a point); parentheses are the only place operators appear, a call’s own parens count (up(5 * r, 10)), and groups may span lines (SPEC 10.7).

Numbers — integer or decimal, optional sign, no units (px for lengths, degrees for angles, 0–1 for opacities/fractions). 10, -5, 0.25, +3. A trailing % makes a percentage (50%), valid only in colour components.

The comma law — CSS’s rule, stated once. A comma separates repeated list items: data: 2, 3, 4 · columns: 80, 140, auto · points: 0 0, 10 10 · categories: "Q1", "Q2" · along: 0.2, 0.5, 0.8 · align: start, center, end. A space separates the components of one item, tuple, interval, or shorthand: padding: 5 2 5 5 · shadow: 2 2 4 #0003 · translate: 10 -4 · range: 0 100 · cell: 2 1 — and so data: 10 20, 30 40 is two x y points, and a lone data: 10 20 is one point, never two values (a value list is comma-separated: data: 9, 15, 24). A pipeline of calls that folds into one value stays space-separated, like CSS transformdraw: move(0,0) up(8) fillet(2), and mirror:, whose items each reflect the union so far. Functions use parentheses and sit in value position — rgb(…), hsl(…), repeat(…), the math library, and any you bind (SPEC 10). A call’s ( glues to its name (rgb(…), never rgb (…)); a free-standing (…) is a math group, and a free-standing (-), (o), or (<) a measuring op (SPEC 15.6) — which is how move(-2, 5), (8 * 2), and pin (o) never meet.

Colours#fff, #f80c, #ffaa00, #ffaa00cc (3/4/6/8 hex digits; the 4- and 8-digit forms carry alpha), CSS names (red, cornflowerblue), rgb(…), rgba(…), hsl(…), hsla(…) (percentages allowed — hsl(200, 50%, 50%)), oklch(L, C, H[, A]) (the palette’s own space — L/A in 0–1, C the chroma, H in degrees; folded to a hex at compile time, so it renders in every target), a --name variable reference, or none. Out-of-range channels are an error. Beyond a flat colour, a paint (fill / stroke / gap-fill) may be a gradientgradient(…), linear-gradient(…), or radial-gradient(…) — reached, like the built-in hue palette, through the colour system (SPEC 10).