Esc

comark-comments

Suggested edits and comments on a span of text, each with its note in a card in the margin, level with its line.

Playground

To log insign in⁠ClaudeThe button says Sign in., simply⁠Claude“Simply” tells a stuck reader it should have been easy. enter the email you signed up with and we’ll send you a link. The link expires after 15 minutes⁠onmaxIs 15 minutes enough when email is slow? Support gets tickets about expired links.ClaudeKeeping 15. The email has a resend button, and a longer-lived link is riskier.atinuxAgreed. Mention the resend button in the email itself. and works once⁠CodexPeople reuse old links and hit an error..

In the event that you forgetIf you forget⁠ClaudeShorter, same meaning. which email you used, ask a workspace admin⁠farnabazCan we link to the list of admins here?.

Your session lasts 30 days on this device. Signing out on one device doesn’t sign you out on the othersyour other devices.

  • A suggestion strikes the old words and inserts the new ones, as a redline or in a hand-drawn pen.
  • A note opens a card in the margin, level with its line. Crowded cards stack down without opening gaps in the text.
  • A reply written anywhere in the document joins its thread, and the bubble counts the messages.
  • On a phone, a click on a bubble opens its card under the line. In print, the cards stay in a narrower margin.
  • No script. An avatar is the author's image from avatars, or their initial on a color picked from the name.
  • A screen reader hears "removed" and "added" around each change, and each card is a note that starts with its author.

Write :suggestion with the new text in its brackets and the old text in from:

Playground

To log insign in⁠The button says Sign in., enter your email.

Use empty brackets, or none, with from to suggest removing words:

Playground

Simply⁠It tells a stuck reader it should have been easy. Enter your email.

Leave out from to suggest adding words:

Playground

The link expires after 15 minutes and works once⁠People reuse old links..

Use the note prop to say why. A suggestion with no note and no replies is only a redline, with no card and no bubble:

Playground

Signing out doesn’t sign you out on the othersyour other devices.

Use the by prop to name the author. It shows without a leading @, beside their initial or the image the plugin's avatars gives that name:

Playground

To log insign in⁠onmaxThe button says Sign in., enter your email.

Use the mark prop to draw a suggestion with a hand-drawn pen, which strikes the old words and writes the new ones above them. An insertion hangs over a caret.

Playground

To log insign in⁠The button says Sign in., enter your email.

Note

Pen only draws short changes, with both sides 24 characters or fewer, on one line, and plain text with no code or links. Others draw as track.

Write :comment with the words it's about in its brackets and a note:

Playground

The link expires after 15 minutes⁠onmaxIs 15 minutes enough when email is slow?.

Give a comment or suggestion an #id, then write :comment{reply="id"} with a note anywhere in the document. The reply joins that thread, and its empty paragraph is dropped:

Playground

The link expires after 15 minutes⁠onmaxIs 15 minutes enough?ClaudeKeeping 15. The email has a resend button..

Pass mark to the plugin to draw every suggestion with the pen. A suggestion's own mark still wins:

import comarkUi from "comark-ui/html";

const { plugins, components } = comarkUi({ comments: { mark: "pen" } });
ts

Critic reads CriticMarkup, inline marks with no notes that a host shows, accepts, or rejects with its mode. Comments add an author, a reason in the margin, and replies. Use critic to publish an edited draft, and comments for a review someone answers.

Each deletion and insertion has hidden text, "removed: …" and "added: …", because most screen readers say nothing for <del> and <ins>. Each card has role="note" and starts with its author.

The bubble is a checkbox named by its thread, such as "2 comments on “15 minutes”". Tab reaches it. Space pins the card's highlight, or opens the card under the line on a narrow page.

[new]

The new text, in the brackets. Leave it empty, or leave out the brackets, for a deletion.

from

The text it replaces. Leave it out for an insertion.

by

The author, shown without a leading @.

note

Why, in plain text. Without it and without replies there is no card.

markDefault: track

How it draws: track or pen.

#id

The id a reply points at.

[anchor]

The words it's about, in the brackets.

by

The author, shown without a leading @.

note

The comment, in plain text.

#id

The id a reply points at.

reply

The #id of the comment or suggestion it answers.

by

The author, shown without a leading @.

note

The reply, in plain text.

markDefault: track

How suggestions draw unless one sets its own mark: track or pen.

avatarsDefault: {}

An image URL per author, by the name in by without @, in any case, such as { onmax: "https://github.com/onmax.png" }.

comments: false in comarkUi() turns it off.

The plugin adds a problem with any suggestion, comment, or reply to meta.issues, as Validation describes:

reply to "tll" matches no comment or suggestion's `#id`, so it renders nothing. Did you mean `reply="ttl"`?
txt

It reports:

  • A prop it doesn't know, such as reason for note.
  • A suggestion with no new text and no from, which renders nothing.
  • A suggestion whose from is its new text.
  • A mark other than track or pen.
  • A comment with nothing in its brackets, which renders nothing.
  • A comment or a reply with no note.
  • An #id two threads share. Replies go to the first.
  • A reply that matches no #id. It renders nothing.
  • A reply with text in its brackets. The text stays where it is.
  • A comment or suggestion inside another one, or in a heading or a table cell, where it gets no card.

Each one has its own rule in Doctor.

comark-ui brings comark-comments and registers it in comark-ui/html. To use it alone, install it and pass the renderer's components:

pnpm add comark @comark/html comark-comments
import { createHtmlRenderer } from "@comark/html";
import comments, { components } from "comark-comments/html";

const render = createHtmlRenderer({ plugins: [comments({ mark: "track" })], components });
ts

Comments render only to HTML. Renderers covers the rest.

The cards and bubbles follow the shared tokens in Theming. The rules sit in a comark CSS layer, so any plain rule of yours wins, except the margin each block leaves on a wide page. That one is !important so a page's paragraph margins can't close it.

The renderer adds the styles to every document with a comment or suggestion. To draw the same marks, bubbles, or cards outside a rendered document, such as review notes over a page, import them with yours:

import "comark-comments/style.css";
ts

Or inline the same styles from css:

import { css } from "comark-comments";
ts

comark-ui re-exports css as commentsCss from every entry. The pen's handwriting font isn't in them; the renderer adds it to a document with a pen mark.

Token Used for
--comark-background The cards and bubbles
--comark-foreground The cards' text, and a highlighted bubble
--comark-muted The bubbles' count
--comark-border The cards' and bubbles' borders, and the line between replies
--comark-ring A highlighted card or bubble, and the focus ring
--comark-radius The cards' corners
--comark-shadow A card while it is open beside the text
--comark-font The cards' and bubbles' text

Set these on .cr or any ancestor, such as :root:

Variable Default Used for
--cr-del --comark-danger Deleted words, and the pen
--cr-ins --comark-success Inserted words
--cr-comment amber A comment's highlight
--cr-icon a grey speech bubble The bubble's icon, as a CSS image

An avatar without an image is the author's initial on a tint. The renderer sets --cr-hue on each one, a hue from 0 to 360 picked from the name, and the styles mix it into the page's colors. A host that draws its own avatars, such as for a comment it adds live, sets --cr-hue on each .cr-av too. Without it, the circle has no background and the letter takes the text color:

<span class="cr-av" style="--cr-hue: 145" aria-hidden="true">M</span>
html
Selector Is
.cr The root around the document's blocks, when any card goes in the margin
.cr-sg A suggestion, with data-mark (track or pen) and data-kind (replace, delete, or insert)
.cr-cm A comment's anchor, a <mark>
.cr-bub The bubble, a <label> for the hidden .cr-tg checkbox
.cr-card The card
.cr-entry One message in a card: the note, then each reply
.cr-av An avatar: an <img>, or a <span> with the author's initial on a tint of its --cr-hue

Blue comments:

:root {
  --cr-comment: #3b82f6;
}
css
Built on Comark. Margin comments as in Google Docs and Microsoft Word. The handwriting is Caveat, by Impallari Type.Made by Maxi García