KQ-1607 · MARGIN/PENCIL

Margin Pencil

A paragraph of real text and a careful reader with a coloured pencil: when it comes into view the pencil marks it up mark by mark — an underline, a loop that overshoots its own start, a bracket in the gutter with a note, a phrase struck through — each built from the phrase's real line fragments so it sits on the words wherever they wrap. Every stroke is a ribbon whose width follows the hand's pressure, with a seeded wobble, and the pen travels it on a minimum-jerk profile timed from its length, lifting between strokes, while a small pencil rides the tip; notes are written line by line in a margin column, or as numbered footnotes when the container is narrow. Hovering a phrase brings its mark and note up on a short tween; there is nothing to operate, so the keyboard reads the marks and notes in place from sr-only annotations.

KQ-1607 · MARGIN/PENCIL

Gaugeworks · launch note · draft 2

Our new sensors report every reading (circled; note: really every?) the moment it lands, so the whole (struck through) team sees the same numbers (underlined). Setup takes about five minutes (bracketed; note: prove it), and nothing else on the line needs to change.

0 of 4 marks · reading

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/margin-pencil

Props

children / text
string
default —
The paragraph, as children or a prop.
marks
MarginPencilMark[]
default []
What the reader marks: { phrase, kind: "underline" | "circle" | "bracket" | "strike", note?, occurrence? }; overlapping marks after the first are skipped.
as
"p" | "div" | "blockquote"
default "p"
The element that carries the paragraph.
pencil
"red" | "blue" | "graphite" | "green" | string
default "red"
The colour of the marks: a named pencil or any CSS colour.
markStyle
"pencil" | "ink" | "marker"
default "pencil"
The instrument: a coloured pencil, a fountain pen or a felt marker.
pace
number
default 1
How fast the hand works, 0.5 to 2.
pressure
number
default 0.6
How much a stroke's width follows the hand's pressure, 0 to 1.
wobble
number
default 0.5
How much the hand wobbles, 0 to 1.
notes
"auto" | "margin" | "footnotes"
default "auto"
Where notes go: the margin, footnotes, or the margin when there is room.
showPencil
boolean
default true
Draw the pencil at the tip of the stroke.
trigger
"mount" | "inView" | "manual"
default "inView"
When the marking starts.
once
boolean
default true
Mark once, or again each time the paragraph comes back into view.
play
boolean
default false
With trigger "manual": true marks it up, again each time it turns true.
delay
number
default 250
Wait before the first stroke, ms.
seed
number
default 3
Seeds the hand's wobble and pressure.
sound
boolean
default false
A pencil scratch while it draws, for runs the visitor starts.
onMarkDrawn
(index: number) => void
default —
A mark finished drawing: its index in marks.
onComplete
() => void
default —
Every mark is drawn.
className
string
default —
Size and layout: the marks are drawn in em off the paragraph's font size.