Identifiers
How `#id` names a construct, and the two ways to reach what it names.
#Naming a construct
Any construct may carry an identifier, written with # in its head before the implicit value: curve#flight, canvas.graph#projectile, #islands, #accent. It is universal — even directives with no use for one accept it — because a uniform rule with harmless cases beats a per-construct list of what may be named.
#One namespace, document-wide
Every #id in a document shares a single namespace, and identifiers resolve in both directions: a reference may point at something declared later. Two constructs claiming the same id is a collision, reported as an error — #apple and curve#apple cannot coexist.
#Bindings
A binding is a reference written bare — #flight, or with a member, #islands.area. It is never resolved to a value at compile time: the pointer survives into the renderer, and whatever consumes it decides what to do with it.
continue: #earlier-pane
cue.trace{ point(x: 0; y: 0) }(path: #flight)
table(data: #islands; columns: island, area)
text(label: #fit.slope)Because nothing is resolved early, a binding is live — it reflects animated attributes, reader-set parameters and derived values as they change. Liveness is not something a binding declares; it is simply what remains when compile-time resolution is absent.
#Substitutions
{{ }} is the other way to reach a value, and it is a different mechanism entirely: a preprocessor that splices text into the source before parsing. That is why it works identically in prose, attribute values and code bodies.
{{z-expected}} an @let constant
{{#survey.area}} a dataset column, spliced as a comma list
{{#flight.colour}} a plain, unanimated attribute{{#fit.slope}} is a compile error, and so is any animated or reader-manipulated attribute — a snapshot frozen into the document would disagree with what the reader is looking at. Those must be reached by a binding instead.#Member access
The . sigil reaches into whatever the identifier names: a dataset's column (#islands.area), a construct's attribute (#flight.colour), or a derived value (#fit.slope). It is the same operation as namespace access in a type name like canvas.graph — reaching into a container for something it holds — and the language draws no distinction between the two.
- Column references must be fully qualified. There is no bare-column shorthand, because attribute values are unquoted and
label: islandmust stay the literal string. - Existence is proven at compile time even though values are not — a reference to a construct or member that does not exist is an error, so the renderer never meets a dangling pointer.