A package for marking and annotating in math blocks in Typst.
Typst
87
109 commits
updated Jun 27, 2026
A Typst package for marking and annotating elements within math blocks.
For comprehensive documentation, please refer to the manual.
$
markhl(x) + markhl(y, #blue, #<tag1>)
#annot(<tag1>)[Annotation]
$
#set page(width: auto, height: auto, margin: (left: 4cm, top: 2cm, rest: 1cm), fill: white)
#set text(24pt)
$
markul(p_i, #<p>)
= markrect(
exp(- mark(beta, #<beta>, #red) mark(E_i, #<E>, #green)),
#<fac>, #blue,
) / markhl(sum_j exp(- beta E_j), #<Z>)
//
#annot(<p>, pos: bottom + left)[Probability of \ state $i$]
#annot(<beta>, pos: top + left, dy: -1.5em, leader-connect: "elbow")[Inverse temperature]
#annot(<E>, pos: top + right, dy: -1em)[Energy]
#annot(<fac>, pos: top + left)[Boltzmann factor]
#annot(<Z>)[Partition function]
$
#import "@preview/cetz:0.5.2"
#set page(width: auto, height: auto, margin: (x: 4cm, top: 2cm, bottom: 2cm), fill: white)
#set text(24pt)
#let markhl = markhl.with(stroke: 1pt)
$
markhl(1 mark(., #<sep>) 23, #<mantissa>, #red)
markub(
mark(times, #<prd>)
mark(10, #<base>)^mark(4, #<exp>),
#<pow>, #blue, bracket: brace.b,
)
#{
annot(<pow>, dy: 0em, annot-text-props: (size: .9em))[power]
let annot = annot.with(leader-tip: tiptoe.triangle, leader-toe: none)
annot(<mantissa>, pos: left, dx: -.5em, dy: -1em, annot-text-props: (size: .9em))[mantissa]
let annot = annot.with(leader-stroke: .03em, leader-tip: none, leader-toe: none)
annot(<sep>, pos: bottom + left, dx: -.5em)[decimal \ separator]
annot(<prd>, pos: top, dx: -1em, dy: -1.2em)[product]
annot(<base>, pos: top, dy: -1em)[base]
annot(<exp>, pos: top + right, dx: 1em)[exponent]
}
$
#import "@preview/cetz:0.5.2"
#set page(width: auto, height: auto, margin: (y: 2cm, bottom: 1cm), fill: white)
#set text(24pt)
#let rmark = mark.with(color: red)
#let bmark = mark.with(color: blue)
#let pmark = mark.with(color: purple)
$
( rmark(a x, #<ax>) + bmark(b, #<b>) )
( rmark(c x, #<cx>) + bmark(d, #<d>) )
= rmark(a c x^2) + pmark((a d + b c) x) bmark(b d)
$
#annot-cetz(
(<ax>, <b>, <cx>, <d>),
cetz,
{
import cetz.draw: *
set-style(mark: (end: "straight"))
bezier-through("ax.south", (rel: (x: 1, y: -.5)), "cx.south", stroke: red)
bezier-through("ax.south", (rel: (x: 1, y: -1)), "d.south", stroke: purple)
bezier-through("b.north", (rel: (x: .6, y: .5)), "cx.north", stroke: purple)
bezier-through("b.north", (rel: (x: .6, y: 1)), "d.north", stroke: blue)
},
)
Import the package mannot at the top of your document:
#import "@preview/mannot:0.4.0": *
To decorate content within math blocks, use the following marking functions:
mark: Changes the text color.markhl: Highlights the content.markrect: Draws a rectangle around the content.markul: Underlines the content.markuw: Draws a wavy line under the content.markub: Draws a bracket under the content.You can customize the marking color and other styles:
After marking content with a tag (label),
you can later annotate it using the annot function:
$
mark(x, #<tag>) + markhl(f(x), #<0>)
//
#annot(<tag>)[Annotation]
#annot(<0>, pos: top, dy: -1em)[Another Annotation]
$
CAUTION
Theannotfunction must be called within the same math block as the marked content. Using it outside the math block triggers unnecessary layout updates, which may result in a layout non-convergence error.
Markings and annotations do not affect the layout, so you might sometimes need to manually insert spacing before and after the equations to achieve the desired visual appearance:
You need to insert spacing
#v(1em) // <- Manual spacing.
$
mark(x, #<1>, #green)
#annot(<1>, pos: top + right)[Annotation]
#annot(<1>, dy: 1em)[Annotation]
$
#v(2em) // <- Manual spacing.
before/after the equations.
The annot function offers the following arguments to control annotation placement:
pos: Where to place the annotation relative to the marked content.
This can be:
dx, dy: The horizontal and vertical displacement of the annotation's anchor
from the marked content's anchor.
When the annotation is far from the marked content, a leader line is drawn by default.
You can customize its appearance using the following annot arguments:
leader: Whether to draw a leader line. Set to auto to enable automatic drawing based on distance.
leader-stroke: How to stroke the leader line e.g., 1pt + red.
leader-tip, leader-toe: Define the end and start markers of the leader line.
Leader lines are drawn by the tiptoe package.
You can specify markers or none:
$
markhl(x, #<1>)
#annot(
<1>, pos: bottom + right, dy: 1em,
leader-tip: tiptoe.circle,
leader-toe: tiptoe.stealth.with(length: 1000%),
)[annotation]
$
For more options, see the tiptoe page.
leader-connect: How the leader line connects to the marked content and the annotation.
This can be:
anchor-inset: How much to pad the marked content's boundary (anchor boundary) for this annotation.
This can be specified as a single length or a dictionary. It is primarily useful for adjusting the gap between the marked content (including its borders/decorations) and the leader line.
$
markhl(x, #<1>)
#annot(<1>, dy: 1em, anchor-inset: 5pt)[annotation]
$
You can also annotate multiple marked elements simultaneously
by passing an array of their tags to the annot function.
$
mark(x, #<1>, #green)
+ markhl(f(x), #<2>, #purple, stroke: #1pt, radius: #10%)
+ markrect(e^x, #<3>, #red, outset: #.2em)
+ markul(x + 1, #<4>, #gray, stroke: #2pt)
//
#annot((<1>, <2>), dy: 1em)[Annotation]
#annot((<3>, <2>, <4>), pos: top, dy: -1em, leader-connect: "elbow")[Another annotation]
$
To create annotations using the CeTZ canvas, use the annot-cetz function.
This allows you to embed a CeTZ canvas directly onto previously marked content.
Within the CeTZ canvas code block,
you can reference the position and dimensions of the marked content using an anchor with the same name as its tag.
For elements marked with multiple tags, corresponding anchors will be available.
#import "@preview/cetz:0.5.2"
$
mark(x, #<x>) + mark(y, #<y>)
#annot-cetz((<x>, <y>), cetz, {
import cetz.draw: *
content((0, -1), [CeTZ], anchor: "north-west", name: "a")
line("x", "a") // You can refer the marked content.
line("y", "a")
})
$
Use the with function to create new functions with modified default arguments.
For example, to always use elbow-shaped leader lines for annotations:
#let annot = annot.with(leader-connect: "elbow")
markuw and markub functions.color and tag as positional arguments in marking functions without explicit parameter names (e.g., mark(x, red, <tag>)).anchor-inset parameter to annot to adjust the spacing between the marked content and the leader line (Issue #11).&) did not work inside the mark function (Issue #9).core-mark where nested marks inside fractions caused shifted bounding boxes (Issue #10).mark is now markhl (for highlighting).marktc is now mark (for changing text color).annot function.path functions with curve.mannot-init function.markrect, markul and marktc.109 commits
Typst
99.1%
A package for marking and annotating in math blocks in Typst.
Typst
87
109 commits
updated Jun 27, 2026
A Typst package for marking and annotating elements within math blocks.
For comprehensive documentation, please refer to the manual.
$
markhl(x) + markhl(y, #blue, #<tag1>)
#annot(<tag1>)[Annotation]
$
#set page(width: auto, height: auto, margin: (left: 4cm, top: 2cm, rest: 1cm), fill: white)
#set text(24pt)
$
markul(p_i, #<p>)
= markrect(
exp(- mark(beta, #<beta>, #red) mark(E_i, #<E>, #green)),
#<fac>, #blue,
) / markhl(sum_j exp(- beta E_j), #<Z>)
//
#annot(<p>, pos: bottom + left)[Probability of \ state $i$]
#annot(<beta>, pos: top + left, dy: -1.5em, leader-connect: "elbow")[Inverse temperature]
#annot(<E>, pos: top + right, dy: -1em)[Energy]
#annot(<fac>, pos: top + left)[Boltzmann factor]
#annot(<Z>)[Partition function]
$
#import "@preview/cetz:0.5.2"
#set page(width: auto, height: auto, margin: (x: 4cm, top: 2cm, bottom: 2cm), fill: white)
#set text(24pt)
#let markhl = markhl.with(stroke: 1pt)
$
markhl(1 mark(., #<sep>) 23, #<mantissa>, #red)
markub(
mark(times, #<prd>)
mark(10, #<base>)^mark(4, #<exp>),
#<pow>, #blue, bracket: brace.b,
)
#{
annot(<pow>, dy: 0em, annot-text-props: (size: .9em))[power]
let annot = annot.with(leader-tip: tiptoe.triangle, leader-toe: none)
annot(<mantissa>, pos: left, dx: -.5em, dy: -1em, annot-text-props: (size: .9em))[mantissa]
let annot = annot.with(leader-stroke: .03em, leader-tip: none, leader-toe: none)
annot(<sep>, pos: bottom + left, dx: -.5em)[decimal \ separator]
annot(<prd>, pos: top, dx: -1em, dy: -1.2em)[product]
annot(<base>, pos: top, dy: -1em)[base]
annot(<exp>, pos: top + right, dx: 1em)[exponent]
}
$
#import "@preview/cetz:0.5.2"
#set page(width: auto, height: auto, margin: (y: 2cm, bottom: 1cm), fill: white)
#set text(24pt)
#let rmark = mark.with(color: red)
#let bmark = mark.with(color: blue)
#let pmark = mark.with(color: purple)
$
( rmark(a x, #<ax>) + bmark(b, #<b>) )
( rmark(c x, #<cx>) + bmark(d, #<d>) )
= rmark(a c x^2) + pmark((a d + b c) x) bmark(b d)
$
#annot-cetz(
(<ax>, <b>, <cx>, <d>),
cetz,
{
import cetz.draw: *
set-style(mark: (end: "straight"))
bezier-through("ax.south", (rel: (x: 1, y: -.5)), "cx.south", stroke: red)
bezier-through("ax.south", (rel: (x: 1, y: -1)), "d.south", stroke: purple)
bezier-through("b.north", (rel: (x: .6, y: .5)), "cx.north", stroke: purple)
bezier-through("b.north", (rel: (x: .6, y: 1)), "d.north", stroke: blue)
},
)
Import the package mannot at the top of your document:
#import "@preview/mannot:0.4.0": *
To decorate content within math blocks, use the following marking functions:
mark: Changes the text color.markhl: Highlights the content.markrect: Draws a rectangle around the content.markul: Underlines the content.markuw: Draws a wavy line under the content.markub: Draws a bracket under the content.You can customize the marking color and other styles:
After marking content with a tag (label),
you can later annotate it using the annot function:
$
mark(x, #<tag>) + markhl(f(x), #<0>)
//
#annot(<tag>)[Annotation]
#annot(<0>, pos: top, dy: -1em)[Another Annotation]
$
CAUTION
Theannotfunction must be called within the same math block as the marked content. Using it outside the math block triggers unnecessary layout updates, which may result in a layout non-convergence error.
Markings and annotations do not affect the layout, so you might sometimes need to manually insert spacing before and after the equations to achieve the desired visual appearance:
You need to insert spacing
#v(1em) // <- Manual spacing.
$
mark(x, #<1>, #green)
#annot(<1>, pos: top + right)[Annotation]
#annot(<1>, dy: 1em)[Annotation]
$
#v(2em) // <- Manual spacing.
before/after the equations.
The annot function offers the following arguments to control annotation placement:
pos: Where to place the annotation relative to the marked content.
This can be:
dx, dy: The horizontal and vertical displacement of the annotation's anchor
from the marked content's anchor.
When the annotation is far from the marked content, a leader line is drawn by default.
You can customize its appearance using the following annot arguments:
leader: Whether to draw a leader line. Set to auto to enable automatic drawing based on distance.
leader-stroke: How to stroke the leader line e.g., 1pt + red.
leader-tip, leader-toe: Define the end and start markers of the leader line.
Leader lines are drawn by the tiptoe package.
You can specify markers or none:
$
markhl(x, #<1>)
#annot(
<1>, pos: bottom + right, dy: 1em,
leader-tip: tiptoe.circle,
leader-toe: tiptoe.stealth.with(length: 1000%),
)[annotation]
$
For more options, see the tiptoe page.
leader-connect: How the leader line connects to the marked content and the annotation.
This can be:
anchor-inset: How much to pad the marked content's boundary (anchor boundary) for this annotation.
This can be specified as a single length or a dictionary. It is primarily useful for adjusting the gap between the marked content (including its borders/decorations) and the leader line.
$
markhl(x, #<1>)
#annot(<1>, dy: 1em, anchor-inset: 5pt)[annotation]
$
You can also annotate multiple marked elements simultaneously
by passing an array of their tags to the annot function.
$
mark(x, #<1>, #green)
+ markhl(f(x), #<2>, #purple, stroke: #1pt, radius: #10%)
+ markrect(e^x, #<3>, #red, outset: #.2em)
+ markul(x + 1, #<4>, #gray, stroke: #2pt)
//
#annot((<1>, <2>), dy: 1em)[Annotation]
#annot((<3>, <2>, <4>), pos: top, dy: -1em, leader-connect: "elbow")[Another annotation]
$
To create annotations using the CeTZ canvas, use the annot-cetz function.
This allows you to embed a CeTZ canvas directly onto previously marked content.
Within the CeTZ canvas code block,
you can reference the position and dimensions of the marked content using an anchor with the same name as its tag.
For elements marked with multiple tags, corresponding anchors will be available.
#import "@preview/cetz:0.5.2"
$
mark(x, #<x>) + mark(y, #<y>)
#annot-cetz((<x>, <y>), cetz, {
import cetz.draw: *
content((0, -1), [CeTZ], anchor: "north-west", name: "a")
line("x", "a") // You can refer the marked content.
line("y", "a")
})
$
Use the with function to create new functions with modified default arguments.
For example, to always use elbow-shaped leader lines for annotations:
#let annot = annot.with(leader-connect: "elbow")
markuw and markub functions.color and tag as positional arguments in marking functions without explicit parameter names (e.g., mark(x, red, <tag>)).anchor-inset parameter to annot to adjust the spacing between the marked content and the leader line (Issue #11).&) did not work inside the mark function (Issue #9).core-mark where nested marks inside fractions caused shifted bounding boxes (Issue #10).mark is now markhl (for highlighting).marktc is now mark (for changing text color).annot function.path functions with curve.mannot-init function.markrect, markul and marktc.109 commits
Typst
99.1%