A package for easy-to-use but powerful and smart margin notes.
Usage
The main function provided by this package is sidenote
, which allows you to create margin notes. The function takes a single positional argument (the text of the note) and several optional keyword arguments for customization:
Parameter | Description | Default |
---|---|---|
side | The margin where the note should be placed. | auto |
dy | The custom offset by which the note should be moved along the y-axis. | 0pt |
padding | The space between the note and the page or content border. | 2em |
gap | The minimum gap between this and neighboring notes. | 0.4em |
numbering | How the note should be numbered. | none |
counter | The counter to use for numbering. | counter("sidenote") |
format | The “show rule” for the note. | it => it.default |
The parameters allow maximum flexibility and often allow values of different types:
- The
side
parameter can be set toauto
,"inside"
,"outside"
or any horizontalalignment
value. If set toauto
, the note is placed on the larger of the two margins. If they are equally large, it is placed on the"outside"
margin. - If the
dy
parameter has a relative part, it is resolved relative to the height of the note. - The
padding
parameter can be set either to a single length value or a dictionary. If a dictionary is used, the keys can be any horizontal alignment value, as well asinside
andoutside
. - With the
counter
parameter, you can for example combine the numbering of footnotes and sidenotes.
An especially useful feature is the format
parameter, as it emulates the behavior of a show rule via a function. That function is called with the context of the note and receives a dictionary with the following keys:
Key | Description | Value or type |
---|---|---|
side | The side of the page the note is placed on. | left or right |
numbering | The numbering of the note. | str , function or none |
counter | The counter used for numbering the note. | counter |
padding | The padding of the note, resolved to left and right . | dictionary |
margin | The size of the margin, which the note is placed on. | length |
source | The location in the document where the note is defined. | location |
body | The content of the note. | str |
default | The default look of the note. | content |
As the dictionary itself is not an element, you cannot directly use it within the format
function as you would be able to in a normal show rule. To still be able to build upon the default look of the note without having to reconstruct it, the default
key is provided. The default style sets the font size to 0.85em
and the paragraph’s leading to 0.5em
, matching the default style of footnotes. This can of course be overridden.
Aside from the customizability, the package also provides automatic overlap and overflow protection. If a note would overlap with another note, it is moved further down the page, so that the gap
parameters of both notes are respected. If a note would overflow the page, it is moved upwards, so that the bottom of the note is aligned with the bottom of the page content. Any previous notes, which would then overlap with the moved note, are also moved accordingly.
Note about pages with automatic width
If a note is placed in the right margin of a page with width set to auto
, additional configuration is necessary. As the final width of the page is not known when the note is placed, the note’s position cannot be calculated. To place notes on the right margin of such pages, the package provides a container
, which is supposed to be included in the page’s background
or foreground
:
#import "@preview/marge:0.1.0": sidenote, container
#set page(width: auto, background: container)
...
The use of the container
variable is detected automatically by the package, so that an error can be raised when it is required but not set.
Note about layout convergence and performance
This package makes heavy use of states and contextual blocks, causing Typst to require multiple layout passes to fully resolve the final layout. Usually, the limit imposed by Typst is sufficient, but I cannot guarantee that this will remain true for large documents with a lot of notes. If you happen to run into this limit, you can try using the container
variable as mentioned above, as it can reduce the number of layout passes required.
As each layout iteration adds to the total compile time, the use of the container
can also be beneficial for performance reasons. Another performance tip is to keep the size of paragraphs containing margin notes small, as the line breaking algorithm cannot be memoized when the paragraph contains a note.
Note about how lengths are resolved
When a length is given in a context-dependent way (i.e. in em
units), it is resolved relative to the font size of the content, not the font size of the note (which is smaller by default). This has the unfortunate side effect that a gap set to 0pt
will still have some space due to the content paragraph’s leading (which is also larger than default leading of the note). Similarly, if notes are defined in a context with a larger font size, the padding and gap values may unexpectedly be larger than of neighboring notes.
Example
#import "@preview/marge:0.1.0": sidenote
#set page(margin: (right: 5cm))
#set par(justify: true)
#let sidenote = sidenote.with(numbering: "1", padding: 1em)
The Simpsons is an iconic animated series that began in 1989
#sidenote[The show holds the record for the most episodes of any
American sitcom.]. The show features the Simpson family: Homer,
Marge, Bart, Lisa, and Maggie.
Bart is the rebellious son who often gets into trouble, and Lisa
is the intelligent and talented daughter #sidenote[Lisa is known
for her saxophone playing and academic achievements.]. Baby
Maggie, though silent, has had moments of surprising brilliance
#sidenote[Maggie once shot Mr. Burns in a dramatic plot twist.].