define

@defineDirective

Declares a template — a node shape written once and stamped out wherever its name is used. The implicit attribute node names the type being declared (@define:card); the attribute group declares the template’s own attributes, each with a TYPE written name: !<type>(…), and {{name}} splices the value into the body. body and detail are reserved names that declare the template’s SECTIONS rather than attributes: {{body}} and {{detail}} splice the content of the use’s own {...} and [...]. Because the declarations make a real node type, a use is checked where it is written — a value the type cannot parse is an error at the call site, naming the template. A template may use other templates, but circular expansion is a compile error. An id written literally in the template is private to each use, so two uses never collide; an id spelt through an attribute (figure#{{fid}}) is the caller’s, document-wide.

#Attributes

nodeRequiredImplicit
The name of the type being declared, written as shorthand: @define:card. It cannot also be a template parameter.Can be written as shorthand: @define:value

#Allowed content

Body { }The template — the Chalk that replaces each use, with {{param}} splices and {{body}}/{{detail}} for the use’s own sections

#Examples

@define:swatch{
    square(x: 0; y: 0; size: 1; colour: {{colour}})
}(
    colour: !colour(default: blue)
)

swatch(colour: red)
@define:finding{
    note{
        {{body}}
    }
}

finding{Richness scales as roughly the cube root of area.}
@define:reading{
    point(x: {{x}}; y: {{y}}; label: {{name}})
}(
    x:    !number(required; implicit)
    y:    !number(required)
    name: !string(default:)
)

reading:2(y: 4.1; name: Baltra)

#Notes

  • Each attribute is declared name: !<type>(…), parentheses included even when empty (!string()) — a bare !name is a value fold missing its body. Inside the group, required, default, aliases and implicit describe the ATTRIBUTE; everything else refines the type (!number(min: 0), !reference(targets: @data)). Join alternatives with |.
  • An attribute that is neither required nor given a default binds nothing when a use omits it, so a template that splices it gets the ordinary undefined-constant error. Give every attribute a default unless the use must always supply it.
  • body: and detail: declare the template’s sections, using the same policy forms an element has: !all(), !none(), !literal(), !children(scene, figure), !inline(content: all). They default to body: !all() and detail: !none(), so a template that splices {{detail}} must declare detail: !all() or every use is refused its [...] section.
  • A literal id in the template compiles to id~1, id~2, … numbered by use in document order. A reference written in the template resolves to the same use’s copy first, then outward — the enclosing template’s, then the document’s; a name declared both in the template and outside it is an ambiguity error, never a shadow.
  • Block content passed through a slot keeps the caller’s ids: card{ figure#mine{} } leaves mine document-wide.
  • @data declared inside a template is private per use like any id, and canvas attributes (x: #pts.x) cannot reach a private dataset — declare the @data at document level, or name it through an attribute (@data#{{name}}).
  • preset: does not apply to a use, and a use is not checked against its parent’s content policy — the nodes it expands to are, where they land.