Manual
The document
OverviewGrammarDocument headerSyntax cheatsheet Scene Step
Content
Text blocks
Paragraph Heading 1 Heading 2 Heading 3 Heading 4 List
Maths
Equation Maths
Tables & images
Table Image
Formatting
Bold Italics Inline Code Highlight
Text colours
Links & references
Link Footnote Icon Internote Reference
ValueWhere inline content works
Narrative only
Note Definition Example Task Divider
Graph pane
Graph
Curves & areas
Curve Integral
Data marks
Scatter Histogram
Fits
Fit Residuals
Surfaces
Contour Heatmap
Marks
Line Segment Vector Point Polygon Text
Shapes
Circle Square Rectangle Triangle Star Diamond Hexagon
Axis furniture
Axis label Axis Brace
Expressions
Code pane
Code Lines
Animation & interaction
Cues that wrap content
Cue Draw cue Trace cue Highlight cue Spotlight cue Type cue
Cues on a value
Value cue Zoom cue
Anchors & timing Parameter
Advanced features
Data Let Define Preset Include If For Ignore
Reference
All constructs A–ZAttribute types
Concepts
IdentifiersBody & detailDerived attributes
Guidance
The narrative contractWhat earns a place on the canvasParameter or cueHonest numbersCode demonstrates, never computesAuthor-attached data only
?
  1. The document
  2. Grammar

Grammar

The whole shape of a construct: sigils, the head, the three brackets, and when a line is prose.

#Prose and constructs

A Chalk document is plain text. Prose is prose — a line of writing compiles to a paragraph without ceremony — and everything that is not prose is a construct: a node, a micronode, or a directive. A node is recognised only by an opening bracket, so a line beginning Note: this is important stays prose forever, even though note is an element — nothing opens a bracket. Micronodes and directives carry their own sigil, so they stand bare: !value:#fit.slope and @include:overview.part.chalk are both constructs on their own.

scene{
    # Falling objects
    Galileo's insight was that mass does not matter.
}[
    canvas.graph#drop{
        curve#h(expression: 100 - 4.9x^2)
    }(
        x-axis-label: t
        y-axis-label: height
    )
]

That is a whole valid document: one scene whose body holds the narrative, and whose detail declares a graph pane with a single curve. Every page of this manual is about some part of that shape.

#The head

Before its brackets, every construct is written the same way:

name[#id][:value]

#id assigns an identifier — universal, available on every node, micronode and directive. :value supplies the construct's implicit attribute: a positional shorthand for whichever attribute the element declares first. Both are optional and independent, and the order is fixed: # before :.

curve#flight        #flight names this curve
image:photo.jpg     :photo.jpg fills source, the first attribute image declares

That is the whole of it — an id and one attribute. Everything else is an ordinary attribute written in the group, and the shorthand form and the explicit form mean exactly the same thing:

image#hero:photo.jpg(width: 400)
image#hero(source: photo.jpg; width: 400)

The : value is a simple token: it starts immediately after the colon and ends at the first bracket, whitespace, or end of line. Anything more complex — spaces, brackets, a list — is written as a named attribute instead. And because the name, id and value are read in that fixed order, a # inside the value is just a character, which is what lets !value:#logged.slope parse as a reference.

#The three brackets

  • { } — the body
  • [ ] — the detail, which for a scene is its canvas
  • ( ) — the attribute group

What is legal inside the body and the detail is the element's containment policy — every reference page prints its policy under "Allowed content", and the reverse ("Allowed in") says where the element may be written. Body & detail covers the policies in full.

An empty group () is not a special form — it is an attribute group with nothing in it. A bare node head like image:photo.jpg is prose until a bracket follows; image:photo.jpg() is the node. Micronodes don't need the crutch — their sigil already marks them.

#Attributes

Attributes live in a parenthesised group, separated by semicolons: point(x: 1; y: 2; colour: orange), or one per line when the group is written across several. The group comes last, after the body and detail. The control-flow directives are the exception — @if(audience: teacher){…} and @for(n: 0..4){…} put theirs first.

The implicit attribute is not a special mechanism — it is the first attribute in the element's schema, and both spellings are equivalent: image:photo.jpg is image(source: photo.jpg). Every value in Chalk is a string until an attribute's declared type parses it; the type reference lists what each type accepts.

#Sigils

  • . — namespace and member access: canvas.graph, #islands.area
  • # — assigns or refers to an identifier: curve#flight, path: #flight
  • : — supplies the implicit attribute: image:photo.jpg
  • ! — a micronode: !teal{…}, !value:#f.slope
  • @ — a directive: @let(…), @data#islands
  • * — the document header, at the top of the file
  • {{ }} — compile-time substitution: {{z-expected}}

The cheatsheet puts all of it on one page.

On this page
Prose and constructsThe headThe three bracketsAttributesSigils
PrivacyTermsContact Support
Grammar · Internote