Math, figures and assets
Math
Write math in LaTeX, between dollar signs. The import turns it into MathML, and the browser draws the MathML in the math font of the theme. The learner can select it like text.
| Written | Gives |
|---|---|
$a x + b = 0$ | inline math, in the line of text |
$$ x = 3 $$ | display math, on a line of its own |
\$5 | a literal dollar sign |
Rules for inline math:
- Put no space after the opening
$and no space before the closing$.$ x $stays plain text. - Write a dollar of money as
\$.
Display math can sit in its own paragraph, or in a line of text. Both give display math.
Several lines
A line break \\ works only inside an environment. For a chain of steps, use aligned:
$$
\begin{aligned}
3x - 6 &= 9 \\
3x &= 15 \\
x &= 5
\end{aligned}
$$
A \\ in display math outside an environment is a fault.
What works
These commands work. The list is not complete, but it covers what a course needs:
| Kind | Commands |
|---|---|
| fractions and roots | \frac{a}{b}, \dfrac{a}{b}, \sqrt{x}, \sqrt[3]{x}, \binom{n}{k} |
| scripts | x^2, x_i, x_{i+1}^{2} |
| big operators | \sum_{i=1}^{n}, \prod, \int_0^1, \iint, \oint, \lim_{x \to 0}, \bigcup |
| accents | \vec{v}, \hat{n}, \bar{x}, \dot{x}, \ddot{x}, \tilde{x}, \overline{AB}, \overrightarrow{AB}, \widehat{x} |
| text and fonts | \text{m/s}, \mathrm{d}x, \mathbf{F}, \mathit{x}, \mathsf{x}, \mathtt{x}, \mathbb{R}, \mathcal{L}, \mathfrak{g}, \boldsymbol{x} |
| functions | \sin, \ln, \exp, \log_2, \det, \max, \operatorname{sgn} |
| delimiters | \left( \frac{a}{b} \right), \langle x \rangle, \lfloor x \rfloor, \bigl( \bigr) |
| environments | pmatrix, bmatrix, vmatrix, matrix, array, cases, aligned, align |
| arrows and relations | \to, \implies, \iff, \mapsto, \xrightarrow{f}, \le, \ge, \ne, \approx, \equiv, \propto |
| symbols | Greek letters, \infty, \partial, \nabla, \hbar, \ell, \pm, \times, \cdot, \circ, \degree |
| layout | \underbrace{a+b}_{n}, \overset{!}{=}, \quad, \,, \;, \hspace{1em} |
What does not work
| Written | Write instead |
|---|---|
\newcommand, or any macro of your own | the full command each time |
\textbf{…}, \textit{…}, \mbox{…} | \mathbf{…}, \mathit{…}, \text{…} |
\frac12, an argument without braces | \frac{1}{2} |
\tag, \label | nothing: equations have no numbers |
\boxed, \phantom, \vphantom | nothing |
\ce{…} (chemistry), \SI{…} (units) | \text{H}_2\text{O}, 3\ \text{m} |
\color{…} | nothing: it parses, but math always takes the ink of the page |
LaTeX that does not parse is an error. The renderer would draw a red box in place of the math, so the checker and the import refuse it. The message names the file, the line, the LaTeX and the first parse error (Check your repository):
ERROR ./math/alg/linears/solving.md:6: the LaTeX `\frac{a}` will not render: parsing error: expected a token
The checker reads all math: the prose, the ::: equation blocks, and the symbols and the meanings of the legends. Math that nests deeper than 256 levels is an error too.
Math in other places
- The
::: equationblock of a reference holds plain LaTeX with no dollar signs (References and derivations). - The symbol of a
::: legendrow is math:$I$: current, in amperes. - The labels of a plot take
$math$, with limits (Live plots).
Assets
An asset is a picture that a card shows. It lives in an assets/ directory.
| Rule | Detail |
|---|---|
| where | in an assets/ directory beside any index.md: the root, a domain, a section, a topic or a checkpoint folder. An assets/ directory can hold subdirectories. |
| types | .png, .jpg, .jpeg, .webp, .svg |
| size | 256 KB at most per file (262,144 bytes) |
A picture file outside an assets/ directory, a file of another type in one, and a file over the cap are faults:
ERROR ./math/alg/linears/pic.png: `pic.png` sits outside an `assets/` directory: every asset belongs in one
ERROR ./math/alg/linears/assets/notes.txt: `notes.txt` is not an asset this format carries: use .png, .jpg, .jpeg, .webp or .svg
ERROR ./math/alg/linears/assets/big.png: `big.png` is 300000 bytes, over the 262144-byte cap: shrink it before committing it
Use a picture
Write a Markdown image. The text in the brackets is the name of the picture for a screen reader:

The path is relative to the file that uses it, as in any Markdown tool. It can climb with ../, so a deck can use a picture of its section:

The checker reads each path, and the file it names, at the line that uses it:
| Fault | Message |
|---|---|
| no such file | math/alg/linears/solving.md:6: `assets/none.png` is not in this repository |
| a path that leaves the repository | math/alg/linears/solving.md:6: `../../../../../a.png` leaves the repository |
| a picture on another site | math/alg/linears/solving.md:8: `https://example.com/a.png` is a link to another site, and a card fetches nothing: commit the picture under `assets/` |
The checker reads the content of a picture only when a block uses it. It does not read the content of a picture that nothing uses, but it checks the type and the size of every file in an assets/ directory.
The app fetches nothing when a learner reads. So the import puts each picture into the card that uses it. A raster picture becomes a data: address, which is a third larger than the file. A picture used in three cards is in the compiled domain three times. A domain compiles to 32 MB of HTML at most, pictures included, and the checker refuses a domain past this cap. Keep pictures small, and prefer SVG.
SVG figures
An SVG becomes part of the page, so it follows the light and the dark theme. The import does not copy your file. It reads it, keeps the elements and the attributes it knows, and writes a new SVG from them. Everything else goes, silently.
Elements that stay
svg, g, defs, title, desc, path, line, polyline, polygon, rect, circle, ellipse, text, tspan, marker, clipPath, linearGradient, stop.
Any other element goes, with everything inside it. This includes script, style, image, use, symbol, a, foreignObject, pattern, radialGradient, mask, filter, textPath and every animation. A shape inside a <a> or a <symbol> goes with it. Comments, the XML declaration and the doctype go too.
The first element must be <svg>. Everything after the end of that root element goes.
Attributes that stay
| Kind | Attributes | Allowed values |
|---|---|---|
| names | id | letters, digits, -, _ |
| geometry | viewBox, preserveAspectRatio, width, height, x, y, x1, y1, x2, y2, cx, cy, r, rx, ry, dx, dy, d, points, transform, gradientTransform, offset, markerWidth, markerHeight, refX, refY | numbers, letters, spaces, ., ,, -, +, (, ), % |
| paint | fill, stroke, stop-color | none, currentColor, #rgb, #rrggbb, #rrggbbaa, var(--token), url(#id) |
| opacity and stroke | opacity, fill-opacity, stroke-opacity, stop-opacity, stroke-width, stroke-dasharray, stroke-dashoffset, stroke-miterlimit | as geometry |
| words | fill-rule, stroke-linecap, stroke-linejoin, clipPathUnits, orient | one word, 32 characters at most |
| references | clip-path, marker-start, marker-mid, marker-end | none or url(#id) |
type, on text, tspan, title and desc only | font-size, letter-spacing as geometry; font-style, font-weight, text-anchor, dominant-baseline, baseline-shift, paint-order as words |
Any other attribute goes, and so does a value that breaks its rule. Note these:
stylegoes. Write each property as its own attribute:stroke-width="2", notstyle="stroke-width:2".classgoes, because a class names the styles of the app.- The
widthand theheightof the root<svg>go. The root takes its size from the card and its shape from itsviewBox. - Each
idtakes a prefix from a hash of the file, and eachurl(#…)takes the same prefix. So an id in a figure never matches an id of the page or of another figure. font-familygoes. The text takes the font of the page.hrefandxlink:hrefgo.- A colour name or
rgb()goes.fill="red"goes, and the shape then takes the default fill of SVG, which is black. Write colours as hex. - A value longer than 8,192 characters goes. A long
dof a path then goes, and the path draws nothing. Split a long path into several paths.
An editor such as Inkscape writes many attributes of its own. They go, and the drawing stays if it uses only the attributes above. Export as plain SVG, and check the result in the app.
Theme colours
Three exact colours follow the theme:
| Write | Becomes | Is |
|---|---|---|
#000000 | currentColor | the ink of the page |
#1a1a1a | var(--graph-ink) | the ink of a graph |
#0000ff | var(--graph-blue) | the accent colour |
Write them with six digits. #000 is not #000000 here. Any other colour stays as you wrote it, in both themes, so it can vanish on a dark page. Draw in #000000, and use #0000ff for the one thing that needs an accent.
Size
The app draws an SVG at the width of the card, up to 40rem, centred. The height follows the viewBox, so give each SVG a viewBox. A root without a viewBox takes one from its width and its height when both are plain numbers. Lines keep one stroke width at every size.
Faults
The checker and the import repair a missing end tag without a message. They refuse these, at the line that uses the SVG:
ERROR ./math/alg/linears/solving.md:10: `math/alg/linears/assets/a.svg`: this file holds no <svg> element
ERROR ./math/alg/linears/solving.md:10: `math/alg/linears/assets/a.svg`: this SVG nests deeper than 32 levels: flatten it
ERROR ./math/alg/linears/solving.md:10: `math/alg/linears/assets/a.svg`: this SVG holds more than 20000 elements: simplify it
An example
This is part of gravity-field.svg from Stemin Foundations. It uses only kept elements and attributes, and the ink colour:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 361 259" fill="none" stroke="#000000" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
<title>Earth's field reaching the Moon</title>
<circle cx="129.6" cy="129.6" r="34.0" fill="none" stroke="#000000"/>
<text x="129.6" y="134.1" text-anchor="middle" font-size="12" font-style="normal">Earth</text>
<line x1="197.6" y1="129.6" x2="184.5" y2="129.6" stroke="#000000"/>
<path d="M175.5 129.6 L184.5 126.0 L184.5 133.2 Z" fill="#000000" stroke="none"/>
</svg>
Raster pictures
A .png, .jpg, .jpeg or .webp file shows as an image, at its own size, and 40rem wide at most. It does not follow the theme. Use a raster picture for a photograph, and SVG for a drawing.