comark-annotate

comark-annotate

Mark up screenshots in Markdown with hand-drawn marks, numbered notes, arrows, and before/after comparisons.

Get started
Before
  1. 1
    Five filters don't fit, so the toolbar wraps and pushes the table down.
  2. 2
    Apply sits 6px lower than the chips.
After
  1. 3
    Extra filters collapse into +2 filters.
  2. 4
    Apply lines up with the chips.

Hover a mark or a note to highlight the other. The output is plain HTML and CSS with no script, so a saved copy looks the same years later.

Usage

Register the plugin and pass the HTML renderers as components:

import { createHtmlRenderer } from "@comark/html";
import annotate, { components } from "comark-annotate/html";

const render = createHtmlRenderer({ plugins: [annotate()], components });
const html = await render(markdown);

The renderer inlines its styles once per document and scopes them under .ca. Set --ca-color to change the default mark color.

Components

screenshot

An image with marks on it. Other content inside the block becomes the caption.

  1. 1
    Extra filters collapse into +2 filters.
Prop
src Image URL. Required.
alt What the screenshot shows. Required.
label Heading above the notes.
url The page you captured, shown as a link.
viewport Capture size, like 1440x900. A compare checks that both sides match.

annotation

A mark on the screenshot and its note. The content is the note. Notes in the list under the image are numbered. Side notes sit beside the image and point with an arrow instead.

Accuracy now counts returns.
  1. 1
    Export CSV includes the filters you set.
Prop
x y w h The marked area, as percentages of the image from 0 to 100.
mark box (default), circle, underline, highlight, cross, or check.
side left or right puts the note beside the image with an arrow.
color red (default) for a problem, amber for a warning, green for a fix, blue for information, gray for neutral. Each has a dark-mode value. Any hex color works too.

compare

Two screenshots side by side, labelled Before and After unless they set their own label. Numbers continue from one side to the other, and the pair stacks on narrow screens.

Before
  1. 1
    Apply sits lower than the chips.
After
  1. 2
    Fixed.

Guides

Side notes

side="left" or side="right" moves a note out of the list and next to the image, with a hand-drawn arrow to its mark. Side notes have no number because the arrow links them. On narrow screens they drop under the image with a bar in their color, and the arrows hide.

Notes on the same side need room. When two would overlap, the plugin reports it. Move one to the other side or leave it in the list.

Placing marks

Measure the element in the page you captured and divide by the viewport. Don't estimate from the image.

const r = element.getBoundingClientRect();
const box = {
  x: (r.x / innerWidth) * 100,
  y: (r.y / innerHeight) * 100,
  w: (r.width / innerWidth) * 100,
  h: (r.height / innerHeight) * 100,
};

For a full-page screenshot, add the scroll offset and divide by the page size instead.

Validation

The plugin checks every block while parsing and lists the problems in meta.annotateIssues, so you can reject a document before publishing it.

import { parseMarkdown } from "comark";
import annotate from "comark-annotate";

const { meta } = await parseMarkdown(markdown, { plugins: [annotate()] });
if (meta.annotateIssues.length) throw new Error(meta.annotateIssues.join("\n"));

Each issue names its block and says how to fix it:

screenshot "after.png", annotation 2: the box runs past the image edge (x + w and y + h must stay within 100).

Rendering never throws.

Credits

Comark, by the Comark team, does the hard part: the parser, the component syntax, and the HTML renderer this plugin builds on. The hand-drawn marks come from AnnotationKit.