Expressions
Every number in a file can be computed. Parentheses hold compile-time math, the stylesheet binds names, and the result bakes into the SVG as a literal.
Math where a number goes
{
gap: 24;
|box| { fill: --teal-wash; stroke: --teal-deep; }
}
|box#a| "8 * 2" { padding: (8 * 2); }
|box#b| "sqrt(2) * 60" { width: (sqrt(2) * 60); height: 60; }
|box#c| "clamp" { width: clamp(30, 200, 120); height: 60; }
A ( … ) group is folded away when the file compiles. Inside it you get:
- Arithmetic —
+ - * / ^, and comparisons. - A ternary —
cond ? a : b. - The math library —
sqrt,abs,sin,cos,min,max,clamp,floor,round,pow,ln,exp.
A bare call like clamp(30, 200, 120) needs no group, because a call’s own
parentheses already count as one.
The parens are where operators live for a reason: outside them - is a link
and < is a marker, so the group is what tells the two apart.
Bindings
{
direction: column; gap: 16; align: start;
unit = 60;
step(n) = (unit * 1.4 ^ n);
|bar::box| { height: 34; fill: --amber-wash; stroke: --amber-deep; }
}
|bar| "step(0)" { width: step(0); }
|bar| "step(1)" { width: step(1); }
|bar| "step(2)" { width: step(2); }
|bar| "step(3)" { width: step(3); }
name = value; in the stylesheet binds a scalar; name(params) = ( … ) binds
a function. Both read anywhere a value goes, bare or inside a group.
The two signs do different jobs, and the difference matters:
=binds at compile time. A binding is a number the whole file shares, and it is gone by the time the SVG exists.:sets a live property. That is what becomes a CSS variable.
So the four bars above are one geometric series, and nothing downstream can tell they were ever computed.
Parametric geometry
{
direction: column; gap: 20;
wave(a, f) = (u * 320, a * sin(2 * pi * f * u));
}
|line#sine| { points: (u * 320, 24 * sin(2 * pi * 3 * u)); samples: 64; stroke: --sky-deep; }
|line#named| { points: wave(18, 2); samples: 64; stroke: --rose-deep; }
points: on a |line| or |poly| may be an expression in u. That variable
sweeps 0 → 1, sampled samples: times into a vertex list — which is how you
get a wave, a spiral or a curve with no vertex typed.
A bound function returning a point keeps the formula named, as wave(a, f)
does for the rose line.
Charts bind x the same way for a series’ fn: — Charts has it.
Locals
{ gap: 30; }
|line#hex| {
points: (r = 40; n = 6; a = 2 * pi * round(u * n) / n; (r * cos(a), r * sin(a)));
samples: 7; fill: --purple-wash; stroke: --purple-deep;
}
|line#oct| {
points: (r = 40; n = 8; a = 2 * pi * round(u * n) / n; (r * cos(a), r * sin(a)));
samples: 9; fill: --sky-wash; stroke: --sky-deep;
}
Inside a group, name = expr; binds a local for the rest of that group, and
the final expression is its value — no keyword and no return. A top-level
comma makes that value a point.
So one expression draws a regular polygon: n corners at radius r, sampled
once per corner and once more to close it.
Everything here bakes. An exported SVG never depends on host CSS for a size.
Go deeper
Colour, variables & expressions has the full grammar — constants, scientific notation, and what a value may be.