Each example below is live: change the source and the result under it follows.
Text and structure
The blocks every lesson is made of. Headings give the document its outline; there is no page or section wrapper.
<h1> to <h6>
A heading, level 1 to 6.
One line, with the closing tag on the same line. Inline maths and formatting work inside it. The first <h1> is the document title in a PDF.
Example
<h1>Newton's second law</h1>
<h2>Force and acceleration</h2>
<h3>The case $F = m*a$</h3>
<p>
A paragraph of prose with inline maths and formatting.
All prose goes inside <p>...</p>; a bare line of text is an error. A paragraph may run over several lines and ends at </p>.
Inline formatting: **bold**, *italic*, $maths$, and code between backticks. Each is a flat span on one line; they do not nest. Put a backslash before $, * or a backtick to get the character itself. An unclosed $ or * is shown as plain text, never an error.
Write < and > as they are. Text is escaped when it is drawn, so HTML tags inside a paragraph are shown as text, not run.
Example
<p>The net force is $F = m*a$, where $m$ is the **mass** and $a$ the *acceleration*.</p>
<p>Call `sqrt(2)` for a square root. A ticket costs \$5, and 3 < 5.</p>
<ul> and <ol>
A bullet list or a numbered list.
Each item is one <li>...</li> on its own line, with the same inline maths and formatting as a paragraph. Items cannot hold other blocks and lists do not nest; for richer items use a heading or a callout.
Example
<ul>
<li>The discriminant is $D = b^2 - 4*a*c$.</li>
<li>$D > 0$ gives two distinct real roots.</li>
</ul>
<ol>
<li>Compute $D$.</li>
<li>Apply the quadratic formula.</li>
</ol>
<table>
A table written as rows of cells separated by |.
The first row is the header. Every row must have the same number of cells; the leading and trailing | are optional. A cell takes inline maths and formatting, including values computed in a <calc> block. There is no separator row of dashes.
Example
<table>
| Quantity | Symbol | SI unit |
| Displacement | $s$ | m |
| Velocity | $v$ | m/s |
| Acceleration | $a$ | $\mathrm{m/s^2}$ |
</table>
<callout>
A labelled box: definition, theorem, example, note, exercise, answer, or any other label.
One generic box. type is free text and becomes the label ("Definition.", "Worked example."), so a new kind of box needs no new tag. The body is any blocks: paragraphs, maths, tables, scripts, even another callout.
An exercise's answer is simply the next callout with type="answer". Inside a <slide>, a callout with type="speaker notes" is shown only to the presenter.
Attributes
type -- Free-text label, title-cased when shown. Optional.
cite -- Free-text source shown in brackets after the label, such as "JEE Main 2025". Optional.
Example
<callout type="definition">
<p>**Momentum** is the product of mass and velocity:</p>
$$
p = m*v
$$
</callout>
<callout type="exercise" cite="JEE Main 2025">
<p>Solve $x^2 - 7x + 12 = 0$ by factorisation.</p>
</callout>
<callout type="answer">
<p>$(x-3)(x-4) = 0$, so $x = 3$ or $x = 4$.</p>
</callout>
Mathematics
Maths is written either as a plain expression, the way a calculator or ApasScript takes it ((a+b)/(c+d), sqrt(x), x^2), or as LaTeX (\frac{a}{b}, \vec{F}, \int_0^1). Both work between dollar signs.
$...$
Maths inside a line of text.
Allowed wherever text is: headings, paragraphs, list items, table cells, options, answers and hints. It stays on one line.
A plain expression is typeset for you: (a+b)/2 becomes a fraction, sqrt(x) a root, x^2 a power, * a product. So is an equation whose sides are plain expressions: D = b^2 - 4*a*c, x^2 - 7*x + 12 = 0.
Anything else is passed to the page as LaTeX, exactly as written: vectors, limits, integrals, Greek letters. If either side of an equation is LaTeX, the whole span is. Write a unit as \mathrm{m/s^2}, or it is read as m divided by s squared.
When a <calc> block has given a name a value, $name$ shows the value (see <calc>).
Example
<p>Plain: $(a+b)/(c+d)$, $sqrt(x^2 + 1)$, $3*x^2 - 2*x + 1$, $x^2 - 7*x + 12 = 0$.</p>
<p>LaTeX: $\vec{F} = m\vec{a}$, $\int_0^1 x^2\,dx = \frac{1}{3}$, $v = 12\,\mathrm{m/s}$.</p>
$$ ... $$
Display maths: one equation per line, aligned on the equals sign.
A line holding only $$ opens the block and another closes it. With several lines, each is split on its first = and the equals signs line up.
$$align instead takes the alignment point from an & you place yourself, as LaTeX's align does. Use it when a line has no =, or when the alignment is not at the first one.
Aligned on =
$$
2*x + 3 = 7
2*x = 4
x = 2
$$
Explicit alignment with &
$$align
(x+1)^2 &= x^2 + 2*x + 1
&= x*(x + 2) + 1
$$
<calc>
Silent calculation: computes values and shows nothing; later text can quote them.
The body is ApasScript, one name = expression per line. Nothing is displayed. After it, $name$ anywhere on the page shows the computed value, and an expression such as $2*D$ is worked out from it. All <calc> blocks on a page share one set of names.
Values are exact: 1/3 stays a fraction and sqrt(8) becomes 2sqrt(2). Use $decimal(x)$ to show a decimal.
A span containing = is always shown as a formula and never assigns. Keep calculated names different from letters you use purely as symbols on the same page, or the symbol will be replaced by the value.
Example
<calc>
mass = 12
acc = 3/2
force = mass*acc
</calc>
<p>A body of mass $mass$ kg accelerating at $acc$ $\mathrm{m/s^2}$ needs a force of $force$ N, which is $decimal(force/1000)$ kN.</p>
<table>
| Quantity | Value |
| Mass | $mass$ kg |
| Force | $force$ N |
</table>
<formula>
One formula of a chapter: checked by the engine, shown with its symbols.
The first line is the formula, written as solve takes it (V = I*R, 1/f = 1/v - 1/u). Each line after it describes one symbol as name: meaning | unit; leave the unit out for a pure number. An optional applies: line says when the formula holds. id, topic and name on the tag identify it.
The block is checked where it is compiled: every name in the formula must be described, and with the units given, both sides must come to the same unit. A formula that fails says what is wrong in its place.
Follow it with a worked example for the reader: a <callout type="example"> holding the problem in words, its given values as quantities in <calc>, and the working in <steps>. A student then reads the formula, the values put in with their units and the answer, and never a script.
Example
<formula id="ohms-law" topic="ohms_law" name="Ohm's law">
V = I*R
V: potential difference | V
I: current | A
R: resistance | ohm
applies: a conductor at constant temperature
</formula>
<callout type="example">
<p>A potential difference of 12 V is applied across a resistance of 4 ohm. Find the current.</p>
<calc>
V = quantity(12, "V")
R = quantity(4, "ohm")
</calc>
<steps>
I = solve(V = I*R, I)
</steps>
</callout>
Plots
A plot is a <script> block whose last line is a graph. Build the graph inside quiet { ... }, then name it on the last line so only the picture shows.
graph() fits its axes to the data; graph(xmin, xmax, ymin, ymax) fixes them. Colours are CSS names or hex. Line styles are "solid", "dashed" and "dotted". The full list of functions is on the Plotting page.
Function plot
One or more curves y = f(x) with a legend.
plot(g, "expression in x", xmin, xmax, colour, style, label). The expression is quoted. Add a line per curve.
Example
<script>
quiet {
g = graph()
plot(g, "sin(x)", 0, 2*pi, "tomato", "solid", "sin x")
plot(g, "cos(x)", 0, 2*pi, "steelblue", "dashed", "cos x")
title(g, "Sine and cosine")
xticks(g, 1)
}
g
</script>
Data: line, scatter and area
Measured values held in arrays.
plot(g, xs, ys, ...) joins the points; scatter(g, xs, ys, colour, label) shows them as dots; area(g, xs, ys) fills down to the axis. With one array, x counts 1, 2, 3, ...
Example
<script>
quiet {
t = [0, 1, 2, 3, 4, 5]
v = [0, 9.8, 19.6, 29.4, 39.2, 49]
g = graph()
plot(g, t, v, "steelblue", "solid", "v = 9.8 t")
scatter(g, t, v, "tomato", "readings")
title(g, "Speed of a falling body")
}
g
</script>
Bar chart, pie chart and histogram
Counts by category, parts of a whole, or raw data sorted into bins.
bar(g, xs, ys, colour, label) draws one bar per value. With names for xs, bar(g, ["Mon", "Tue"], ys), each bar is named on the axis; stacked(g) stands several bar series on one another.
pie(g, names, values) draws parts of a whole with each name, value and share beside it; a fourth argument such as 0.6 leaves a hole, which makes a ring.
histogram(g, data, bins) bins raw data itself. graph(w, h) sets the size of any of them in pixels.
Bar chart
<script>
quiet {
g = graph()
bar(g, [1, 2, 3, 4, 5], [4, 9, 2, 7, 5], "goldenrod", "Students")
title(g, "Marks out of 5")
}
g
</script>
Named bars, stacked
<script>
quiet {
g = graph(560, 300)
days = ["Mon", "Tue", "Wed", "Thu", "Fri"]
bar(g, days, [2, 4, 1, 3, 2], "#3987e5", "Lessons read")
bar(g, days, [1, 2, 3, 0, 4], "#d95926", "Questions answered")
stacked(g)
}
g
</script>
Pie chart
<script>
quiet {
p = graph(460, 260)
pie(p, ["Physics", "Chemistry", "Mathematics", "Biology"], [40, 25, 20, 15])
title(p, "Questions by subject")
}
p
</script>
Ring
<script>
quiet {
p = graph(460, 240)
pie(p, ["Correct at the first try", "Correct after another try", "Not correct yet"], [15, 3, 4], 0.6)
}
p
</script>
Histogram
<script>
quiet {
heights = [150, 152, 155, 155, 158, 160, 160, 161, 163, 165, 165, 166, 170, 172, 178]
g = graph()
histogram(g, heights, 6)
title(g, "Heights in a class (cm)")
}
g
</script>
Parametric and polar curves
Curves given by x(t), y(t) or by r(theta).
plotParametric(g, "x(t)", "y(t)", tmin, tmax, ...) and plotPolar(g, "r(theta)", thetaMin, thetaMax, ...). aspect(g, true) gives both axes the same scale, so a circle looks like a circle.
tracePlot(g, seconds) makes every curve of the graph draw itself in over that many seconds, a function from left to right, a parametric or polar curve in the order of its parameter. It works for plot, plotParametric and plotPolar alike; in a PDF the finished curves are shown. In one line: tracePlot(g, "sin(x)", 0, 2*pi, 4), tracePolar(g, "1 + cos(theta)", 0, 2*pi, 4) and traceParametric(g, "3*cos(t)", "2*sin(t)", 0, 2*pi, 4) plot the curve and trace it, with the colour, line type and label after the seconds.
Example
<script>
quiet {
g = graph()
plotParametric(g, "3*cos(t)", "2*sin(t)", 0, 2*pi, "steelblue", "solid", "ellipse")
plotPolar(g, "1 + cos(theta)", 0, 2*pi, "tomato", "solid", "cardioid")
aspect(g, true)
}
g
</script>
Logarithmic axis
Growth and decay over many powers of ten.
logScale(g, "x"), logScale(g, "y") or logScale(g, "both").
Example
<script>
quiet {
n = [1, 2, 3, 4, 5, 6, 7, 8]
cells = [2, 4, 8, 16, 32, 64, 128, 256]
g = graph()
plot(g, n, cells, "seagreen", "solid", "cells")
logScale(g, "y")
title(g, "Doubling every generation")
}
g
</script>
Diagrams
A diagram is a <script> block whose last line is a diagram. Create it with d = diagram(width, height), set xrangeOn and yrangeOn, draw into it with the functions ending in On, and put d on the last line.
Coordinates are ordinary x, y units inside the ranges you set. Angles are in degrees, counter-clockwise from the positive x-axis. Styles come after the geometry, in order: colour, label, then a fill colour (shapes) or arrows ("one", "two", "none"). Leave a label as "" and place it yourself with textOn when its position matters. textOn(d, point, "text") starts the text at the point and runs to the right; text is 14 px high whatever the ranges are, so leave room for it. To centre text on a point (a name inside a box, a tick value under an axis, a charge sign) write labelAtOn(d, point, "text", "center"): with textOn the same coordinates put the text to the right of the point and above it, on top of whatever is there. labelAtOn(d, point, "text", side) puts a label beside a point instead ("above", "below", "left", "right"), clear of what is drawn there. Every function is listed on the Diagrams page.
A diagram is drawn on a white surface, on the web and in a PDF. A shape, label or text with no colour of its own is drawn in dark ink; give it a colour to make it stand out.
Points, segments and circles
The basic shapes and how a diagram is set up.
drawOn(d, shape, colour, label, fill) draws a point, circle, ellipse or line. drawOn(d, pointA, pointB, ...) draws the segment between two points. Turn the axes and grid off for a geometry figure.
Example
<script>
quiet {
d = diagram(480, 360)
xrangeOn(d, -4, 4)
yrangeOn(d, -3, 3)
axesOn(d, false)
gridOn(d, false)
O = point(0, 0)
P = point(2, 0)
drawOn(d, circle(O, 2), "steelblue", "", withAlpha("steelblue", 0.15))
drawOn(d, O, P, "tomato", "r = 2")
drawOn(d, O, "black", "O")
drawOn(d, P, "black", "P")
}
d
</script>
Triangles and angle markers
A polygon with its sides and angles labelled.
polygonOn(d, A, B, C, colour, label, fill) takes any number of corners. angleMarkerOn(d, vertex, pointOnRay1, pointOnRay2, radius, label, colour) draws the arc and its label; rightAngleMarkerOn(d, vertex, pointOnRay1, pointOnRay2, size, label, colour) draws the square. Neither needs you to work out an angle.
Example
<script>
quiet {
d = diagram(480, 380)
xrangeOn(d, -1, 6)
yrangeOn(d, -1, 5)
axesOn(d, false)
gridOn(d, false)
A = point(0, 0)
B = point(5, 0)
C = point(0, 4)
polygonOn(d, A, B, C, "steelblue", "", withAlpha("steelblue", 0.15))
rightAngleMarkerOn(d, A, B, C, 0.4, "", "black")
angleMarkerOn(d, B, C, A, 0.9, "θ", "tomato")
textOn(d, point(2.5, -0.4), "5 cm", "black")
textOn(d, point(-0.7, 2), "4 cm", "black")
textOn(d, point(-0.3, -0.3), "A", "black")
textOn(d, point(5.3, -0.3), "B", "black")
textOn(d, point(-0.3, 4.3), "C", "black")
}
d
</script>
Vectors and force diagrams
Arrows from a point, by components or by length and angle.
drawOn(d, point, vec(dx, dy), colour, label, arrows) draws an arrow by its components. vectorFromOn(d, point, length, degrees, colour, label) draws one by length and direction, and segmentFromOn the same without an arrowhead.
drawOn(d, p1, p2, colour, label) with two points draws a plain line between them, with no arrowhead: a wire, a side, a plate, a ray path. For an arrow from p1 to p2, add "one" after the label; "two" puts a head at both ends (a dimension line).
Free-body diagram
<script>
quiet {
d = diagram(480, 380)
xrangeOn(d, -4, 4)
yrangeOn(d, -4, 4)
axesOn(d, false)
gridOn(d, false)
c = point(0, 0)
polygonOn(d, point(-1, -0.6), point(1, -0.6), point(1, 0.6), point(-1, 0.6),
"gray", "", withAlpha("gray", 0.3))
vectorFromOn(d, c, 3, 90, "seagreen", "N")
vectorFromOn(d, c, 3, 270, "tomato", "mg")
vectorFromOn(d, c, 2.5, 0, "steelblue", "F")
vectorFromOn(d, c, 1.5, 180, "darkgoldenrod", "f")
}
d
</script>
Curves, arcs and text
Smooth paths between points, circular arcs and free labels.
curveBetweenOn(d, a, b, bend, colour, label, style, width) bends a line between two points; bend is signed, and 0 is straight. curveOn(d, p0, control, p1) is a Bezier curve. arcAtOn(d, centre, radius, startDegrees, sweepDegrees, colour) is part of a circle. textOn(d, point, "text", colour, fontSize) places a label.
Example
<script>
quiet {
d = diagram(480, 320)
xrangeOn(d, -1, 7)
yrangeOn(d, -1, 4)
axesOn(d, false)
gridOn(d, false)
A = point(0, 0)
B = point(6, 0)
drawOn(d, A, B, "gray", "")
curveBetweenOn(d, A, B, -0.45, "tomato", "", "dashed", 2)
arcAtOn(d, A, 1.2, 0, 60, "darkgoldenrod")
textOn(d, point(1.5, 0.7), "60°", "darkgoldenrod", 14)
textOn(d, point(3, 3.1), "path of the ball", "tomato", 14)
}
d
</script>
A graph of a function inside a diagram
For a coordinate-geometry figure: axes, a line or curve, and marked points.
Leave the axes and grid on. A line is drawn across the whole diagram; a parabola or other curve is a plot on a graph() (see Plots), which can sit next to a diagram in the same document.
Example
<script>
quiet {
d = diagram(480, 400)
xrangeOn(d, -5, 5)
yrangeOn(d, -5, 5)
A = point(-2, -3)
B = point(3, 2)
drawOn(d, line(A, B), "steelblue", "y = x - 1")
drawOn(d, A, "tomato", "A(-2, -3)")
drawOn(d, B, "tomato", "B(3, 2)")
drawOn(d, circle(point(0, 0), 3), "seagreen", "x² + y² = 9")
}
d
</script>
Solids
Textbook cubes, prisms, cylinders and spheres, with marked corners and edges.
cubeOn(d, origin, size, dx, dy, colour) draws a cube in a slanted view; dx, dy is the offset of the back face. It returns a record whose vertices are the exact corner points, so markers and labels attach to the figure. prismOn, cylinderOn and sphereOn work the same way.
cornerMarkerOn(d, point, label, colour) puts a labelled dot on a corner; edgeMarkerOn(d, a, b, fraction, label, colour) puts one part-way along an edge.
Example
<script>
quiet {
d = diagram(480, 380)
xrangeOn(d, -1, 5)
yrangeOn(d, -1, 4.5)
axesOn(d, false)
gridOn(d, false)
s = cubeOn(d, point(0, 0), 3, 1, 1, "steelblue")
cornerMarkerOn(d, s.vertices[0], "A", "tomato")
edgeMarkerOn(d, s.vertices[0], s.vertices[1], 1/2, "M", "darkgoldenrod")
}
d
</script>
Springs, coils and fields
Ready-made physics shapes and field pictures.
springOn(d, start, end, amplitude, turns, colour), coilOn and inductorOn draw between two attachment points. vectorFieldOn(d, "Fx", "Fy", xmin, xmax, ymin, ymax, nx, ny, mode, scale, colour) draws a grid of arrows; the mode is "direction", "scaled" or "capped". pointChargeLinesOn(d, centre, charge, lineCount, xmin, xmax, ymin, ymax) draws the field lines of one charge, and fieldLineOn traces one line of any field.
Spring and mass
<script>
quiet {
d = diagram(520, 260)
xrangeOn(d, -1, 8)
yrangeOn(d, -2, 2)
axesOn(d, false)
gridOn(d, false)
drawOn(d, point(0, -1.5), point(0, 1.5), "gray", "")
springOn(d, point(0, 0), point(5, 0), 0.4, 8, "steelblue")
polygonOn(d, point(5, -0.7), point(6.4, -0.7), point(6.4, 0.7), point(5, 0.7),
"tomato", "", withAlpha("tomato", 0.3))
textOn(d, point(5.7, 0), "m", "black")
textOn(d, point(2.5, 0.9), "k", "black")
}
d
</script>
Field of a positive charge
<script>
quiet {
d = diagram(420, 420)
xrangeOn(d, -3, 3)
yrangeOn(d, -3, 3)
axesOn(d, false)
gridOn(d, false)
pointChargeLinesOn(d, point(0, 0), 1, 12, -3, 3, -3, 3)
}
d
</script>
Circuits
Resistors, cells, meters and the other circuit symbols, one call each.
Name the corners of the circuit as points, then put one component on each branch: resistorOn(d, a, b, label), cellOn, batteryOn(d, a, b, cells, label), capacitorOn, bulbOn, variableResistorOn, inductorOn(d, a, b, label), acSourceOn, diodeOn and zenerDiodeOn (the triangle points from a to b), meterOn(d, a, b, "A", label) and switchKeyOn(d, a, b, "open", label). Each draws its symbol in the middle of the branch and the lead wires out to both points, so do not draw the zigzag or the leads yourself. A cell's positive (long) plate faces its second point.
The label is an argument, not a separate textOn: the engine puts it beside the symbol and clear of it, outside the circuit by default. Add a side ("above", "below", "left", "right") after the label to choose. wireOn(d, p1, p2, p3) joins the remaining corners, junctionOn(d, p, "A") marks and names a node, currentArrowOn(d, a, b, "I") shows a current on a wire, terminalOn(d, p, "A") ends a wire in an open ring, groundOn(d, p) earths a point and labelAtOn(d, p, "+", "left") writes beside a point.
Three symbols take more than two points. transistorOn(d, base, collector, emitter, "npn", label) draws the transistor and its three leads: put the collector and emitter one above the other and the base to one side, level with their middle. gateOn(d, "nand", inputA, inputB, output, label) draws a logic gate ("and", "or", "not" with one input, "nand", "nor", "xor", "xnor") with its leads; give one gate's output point as the next gate's input to connect them. twoWaySwitchOn(d, pivot, contactA, contactB, "a", label) draws a switch thrown to contact "a" or "b", or "open".
Symbols have a fixed size on the page, so set xrangeOn and yrangeOn first, and choose ranges that give each branch about 80 px or more: at least 2 units when the diagram shows 40 px per unit.
Two resistors in parallel, with an ammeter
<script>
quiet {
d = diagram(420, 300)
xrangeOn(d, -1.5, 7.5)
yrangeOn(d, -1.2, 4.2)
axesOn(d, false)
gridOn(d, false)
A = point(0, 3)
B = point(3, 3)
C = point(6, 3)
D = point(6, 0)
E = point(3, 0)
F = point(0, 0)
cellOn(d, F, A, "12 V")
resistorOn(d, A, B, "4 Ω")
meterOn(d, B, C, "A")
resistorOn(d, C, D, "6 Ω")
resistorOn(d, B, E, "3 Ω", "right")
switchKeyOn(d, E, D, "closed", "K")
wireOn(d, E, F)
junctionOn(d, B, "P")
junctionOn(d, E, "Q")
}
d
</script>
Sliders and animation
A picture the reader can change, or one that moves.
slider(label, min, max, step, start) gives a value the reader can drag; the script runs again with the new value. animate(tmin, tmax, frames, f) calls your function f(t) once per frame; each call draws one frame with the functions that have no On (draw, text, vectorFrom, ...).
Slider
<script>
n = slider("sides", 3, 12, 1, 6)
regularPolygon(point(0, 0), 3, n, 90, "teal", "", withAlpha("teal", 0.3))
</script>
Animation
<script>
quiet {
xrange(-5, 5)
yrange(-5, 5)
}
fn frame(t) {
draw(circle(point(0, 0), 4), "gray")
draw(point(4*cos(t), 4*sin(t)), "tomato", "P")
}
animate(0, 2*pi, 60, frame)
</script>
In a PDF: A slider shows its starting value. An animation has no PDF form.
3D surfaces
A surface z = f(x, y) the reader can turn.
surf("expression in x and y", xmin, xmax, ymin, ymax) draws a shaded surface; mesh a wireframe; surfc adds contour lines on the floor.
Example
<script>
surf("sin(sqrt(x^2 + y^2))", -5, 5, -5, 5)
</script>
In a PDF: None. Export a PNG from the web page and include it with <image>.
Agent and application blocks
Compact result cards for AI agents and applications: status, numbers, sources, data, files and approvals. They are drawn on the web only and left out of a PDF. Quote an attribute value that contains spaces. Rows use | between cells, as in <table>.
<tool>
The status of a step an agent ran.
The body is plain text.
Attributes
name -- What the step was.
state -- running, complete, failed or approval_required.
Example
<tool name="Analyse dataset" state="complete">
Read 1,240 sales rows.
</tool>
<risk>
A notice the reader should see before relying on the result.
The body is plain text.
Attributes
level -- info, warning or danger.
title -- The notice's heading.
Example
<risk level="warning" title="Educational analysis only">
This is general information, not investment advice.
</risk>
<metrics>
A row of headline numbers.
Holds one <metric> line per number. Each takes label, value, and optionally change and tone (positive or negative).
Attributes
title -- Heading for the group. Optional.
Example
<metrics title="Campaign performance">
<metric label="Reach" value="40.5K" change="+12.4%" tone="positive">
<metric label="Conversions" value="1,284" change="-3.1%" tone="negative">
</metrics>
<sources>
A list of the sources behind a result.
Holds one <source> line per source, with optional title, url, publisher, published_at and retrieved_at.
Attributes
title -- Heading for the list. Optional.
Example
<sources title="Data sources">
<source title="Quarterly report" url="https://example.com/report" publisher="Example Company" retrieved_at="Retrieved today">
</sources>
<actions>
Buttons the host application handles.
Holds one <action> line per button, with id, label and the bare flags primary and disabled. The page passes the id to the application; the document itself does nothing.
Attributes
title -- Heading for the group. Optional.
Example
<actions title="Next steps">
<action id="download_png" label="Download PNG" primary>
<action id="copy_chart" label="Copy chart">
</actions>
<dataset>
A preview of an uploaded or loaded dataset.
<column name="..." type="..."> lines declare the columns, then one <row>...</row> per record with its cells in the same order.
Attributes
name -- The dataset's name.
sheet -- Sheet or table name. Optional.
rows -- Total number of rows in the full dataset.
preview_limit -- How many rows to show.
Example
<dataset name="Q3 Sales" sheet="Revenue" rows="1240" preview_limit="8">
<column name="month" type="date">
<column name="region" type="string">
<column name="revenue" type="number">
<row>2026-07-01 | North | 42000</row>
<row>2026-08-01 | West | 38900</row>
</dataset>
<data>
A paged data table with typed columns.
Like <dataset>: <column> lines (with name, an optional display label and type), then <row> lines. Use <table> for a table in a lesson; use this for records an application shows.
Attributes
title -- Heading for the table.
page_size -- Rows per page.
Example
<data title="Monthly revenue" page_size="3">
<column name="month" label="Month">
<column name="revenue" label="Revenue" type="number">
<row>Jan | 42000</row>
<row>Feb | 38900</row>
</data>
<file>
A card for a file the reader can download.
A single opening tag with no closing tag.
Attributes
title -- Card heading.
name -- File name.
type -- Kind of file, such as PDF.
size -- Size as text.
description -- One line about the file.
url -- Where to download it.
link_label -- Text of the link.
Example
<file title="Generated report" name="q3-summary.pdf" type="PDF" size="1.8 MB" description="Generated from Q3 campaign data" url="https://example.com/q3-summary.pdf" link_label="Download">
<social>
A draft social-media post.
The body is the post text, followed by any number of one-line <tag>...</tag> hashtags.
Attributes
platform -- Where it will be posted.
image_url -- Picture shown with the post. Optional.
image_alt -- Description of the picture. Optional.
Example
<social platform="LinkedIn">
Our Q3 campaign reached 40.5K people.
<tag>MarketingAnalytics</tag>
<tag>Growth</tag>
</social>
<approval>
A request for the reader's go-ahead before something is published.
The body is the summary, followed by <action> lines for the choices.
Attributes
title -- The question being asked.
platform -- Where it will be published.
destination -- The page or account.
scheduled_at -- When, as text.
Example
<approval title="Ready to publish?" platform="LinkedIn" destination="Company page" scheduled_at="Today, 6:00 PM">
The post and visual are ready for final review.
<action id="approve_publish" label="Approve and publish" primary>
<action id="edit_draft" label="Edit draft">
</approval>