A comment in a code block explains a line. But it takes space in the code, and readers copy it with the code. An annotation moves the explanation out of the code. The line gets a small numbered marker, and the note opens in a popover when a reader clicks the marker.
Readers see
on: [push, pull_request]jobs: test: runs-on: ubuntu-latestUses the Ubuntu GitHub Actions runner.
steps: - uses: actions/checkout@v7 - uses: astral-sh/setup-uv@v7Installs uv and caches its downloads between runs.
- run: uv run pytest- Uses the Ubuntu GitHub Actions runner.
- Installs
uvand caches its downloads between runs.
You write
```yaml title=".github/workflows/test.yml"on: [push, pull_request]jobs: test: runs-on: ubuntu-latest # [!annotate] Uses the Ubuntu GitHub Actions runner. steps: - uses: actions/checkout@v7 - uses: astral-sh/setup-uv@v7 # [!annotate] Installs `uv` and caches its downloads between runs. - run: uv run pytest```Each [!annotate] comment is gone from the output. A numbered marker is in its place, after the code on the line. Hover over a marker to see its note, or click the marker to keep the note open. Press Escape or click anywhere else to close the notes.
Syntax
Section titled “Syntax”| Syntax | Where |
|---|---|
[!annotate] note |
Comment at the end of a line |
startNoteNumber={N} |
Code block fence line |
annotations.style="filled", annotations.style="outline" |
Code block fence line |
The directive goes at the end of the line it explains. The note is the rest of the comment. It can hold inline code, links and bold text. The markers count from 1, in line order. To continue the numbers of an earlier block, add startNoteNumber={N} to the fence line, and the first marker is N. annotations.style sets the style of the markers for one block.
Examples
Section titled “Examples”A link in a note
Section titled “A link in a note”A note can hold a link, for a line that needs more explanation than a sentence:
export async function retry<T>(task: () => Promise<T>, attempts = 3): Promise<T> { for (let i = 1; ; i++) { try { return await task(); } catch (error) { await new Promise((done) => setTimeout(done, 2 ** i * 100));Waits 200 ms, then 400 ms: an exponential backoff.
} }}- Gives up and passes on the last error. See Error handling.
- Waits 200 ms, then 400 ms: an exponential backoff.
```ts title="retry.ts"export async function retry<T>(task: () => Promise<T>, attempts = 3): Promise<T> { for (let i = 1; ; i++) { try { return await task(); } catch (error) { if (i >= attempts) throw error; // [!annotate] Gives up and passes on the last error. See [Error handling](https://example.com/errors). await new Promise((done) => setTimeout(done, 2 ** i * 100)); // [!annotate] Waits 200 ms, then 400 ms: an exponential backoff. } }}```The outline style
Section titled “The outline style”The outline style, for one block:
const port = 8080The default port. Set PORT to change it.
- The default port. Set
PORTto change it.
```js annotations.style="outline"const port = 8080 // [!annotate] The default port. Set `PORT` to change it.```Behaviour
Section titled “Behaviour”- Opening a note:
- Hover over a marker to show its note. The mouse cursor can move into the note, for example to click a link.
- Click the marker to keep the note open. Click it again to close the note.
- Several notes can be open at once. Escape, or a click outside the notes, closes them all.
- Where the note opens:
- To the right of the marker, over the empty space after the line. A long note wraps at the popover width, 340 px by default.
- Under the marker, if the note would cover code or go past the edge of the block or the window. On a phone, most notes open here.
- Above the marker, if there is no room under it.
- Screen readers:
- Each marker is a button with the label “Annotation N”. Screen readers read the note directly after it.
- Copy and print:
- The copy button leaves the markers and the notes out. A manual selection leaves the markers out.
- In print, each marker shows its number, and the notes are a numbered list under the block.
- Without JavaScript:
- The browser’s
popoverattribute opens and closes the notes. Each note opens under its marker.
- The browser’s
- Motion:
- The note fades in and out in 160 ms. On hover, the marker changes colour on the same timing, after 80 ms.
- With reduced motion, both change at once.
Options
Section titled “Options”annotations
Section titled “annotations”Adds numbered markers that open a note. Set it to false to turn the feature off.
- Type
false | object- Default
- On
annotations.style
Section titled “annotations.style”Filled markers in the accent colour, or outlined markers in the magenta of the theme. Side annotations use it too. A code block can set its own on its fence line.
- Type
'filled' | 'outline'- Default
'filled'
The filled style draws each marker as a circle filled with the accent colour. The outline style draws an outlined circle in the magenta of the theme, and fills it when the note is open. Footnotes have the same two styles. To use outlined markers on the whole site:
codeblocks({ annotations: { style: 'outline' },});With annotations: false, the plugin does not know the [!annotate] directive. It leaves the comment in the code as written, and logs a build warning. Side annotations are off too.
Limitations
Section titled “Limitations”- The note is part of the source line. Long notes make long lines in the Markdown file.
- An annotation applies to one line. For a note about a range of lines, put it on the first line of the range.
- Readers see a note only when they open it. If readers need every note to follow the code, use footnotes or side annotations.
Related
Section titled “Related”- Inline callouts: a short, always visible note above the line, with an arrow at one name.
- Footnotes: numbered notes listed under the block, all visible at once.
- Side annotations: the same notes in a column beside the code.