Highlighter
Marks drawn by hand over a few words: a marker band behind them, a line under or through them, a box or a loop around them. Each mark draws in the first time the text comes into view, and keeps its shape when the text wraps again.
Examples
In a paragraph
The marker band sits behind the text, so the words stay sharp. The underline waits until the band is done.
The weekly digest collects every decision made in review, so the people who missed the meeting can catch up in five minutes.
Every mark
Five marks, each in a tone that keeps its meaning. Lines and loops go over twice, as a pen would.
- highlightDue on Friday
- underlineRead before the call
- boxTwo seats left
- circleApproved
- strikeDraft pricing
Shown on demand
With show, the marks follow your state: they draw when it turns true and fade out faster when it turns false.
The launch moves from Thursday to Friday at 10:00, after the pricing review.
Across lines
A phrase that wraps gets one mark per line, drawn one line after the other, each for its share of the duration.
Before you merge, check that every screen still reads well in the dark theme and at the smallest phone width.
Installation
npx shadcn@latest add https://ui.ndekocode.dev/r/motion-highlighter.jsonComponent API
Highlighter
| Prop | Type | Default | Description |
|---|---|---|---|
| children | React.ReactNode | The words to mark. Keep them inline: text, links, strong. | |
| action | 'highlight' | 'underline' | 'box' | 'circle' | 'strike' | 'highlight' | The mark to draw. |
| tone | 'primary' | 'success' | 'warning' | 'info' | 'destructive' | 'warning' for highlight, 'primary' for others | The colour token of the mark. |
| strokeWidth | number | 2 | Width of lines and loops, in pixels. The marker band follows the text height. |
| padding | number | 2, 5 for box, 8 for circle | Room between the text and the mark, in pixels. |
| passes | 1 | 2 | 1 for highlight, 2 for others | How many times the pen goes over the mark. |
| duration | number | 0.8 | Seconds to draw the whole mark, shared between the lines it covers. |
| delay | number | 0 | Seconds before drawing starts. |
| show | boolean | Controls the mark. When it is not set, the mark draws once the text is in view. | |
| className | string | Classes for the wrapping span. |
Accessibility and motion
- The marks are a decorative SVG hidden from assistive technology. When a mark carries meaning, such as a price that no longer applies, say it in the text too, or wrap it in
delandins. - Marker bands use the fill tokens at 35% opacity behind the text. Lines use the status text pairs (
text-warning-foreground,text-destructive-ink...), measured for both themes, and the primary colour. - The shapes come from a seeded generator, so a mark keeps its shape when the window is resized and only its position follows the new line breaks.
- With reduced motion, the marks fade in at full length instead of being drawn.
Credits
Inspired by the highlighter of Magic UI. The hand-drawn shapes are our own SVG paths, with no annotation library.