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