motion-marker
A highlighter stroke that sweeps behind a phrase when it scrolls into view, the way you would mark a printed page. The straight stroke follows text that wraps across lines, and shape="wave" draws a wavy one. The text itself never changes.
Preview
Ship motion that feels
Import
import 'motion-components/motion-marker' // just this one
import 'motion-components' // or the whole libraryUsage
<p>Ship motion that feels <motion-marker>physical, not mechanical</motion-marker>.</p>Color
Set --mc-marker-color. A transparent color keeps the text readable.
Every spring is
<motion-marker style="--mc-marker-color: rgb(96 165 250 / 0.35)">interruptible</motion-marker>Wave
Set shape="wave" for a wavy stroke. It keeps its text on one line, so use it on a word or a short phrase.
Motion that feels
<motion-marker shape="wave">hand-drawn</motion-marker>Text that wraps
On a phrase that wraps, every line gets its own stroke, and all of them draw together.
Drop in a tag and
In a sentence
Mix the marks and draw them one after another with delay.
Replace the
<p>
Replace the <motion-strike>manual checklist</motion-strike>
with <motion-marker delay="0.5">one HTML tag</motion-marker>,
<motion-underline delay="1">tested</motion-underline>
and <motion-ring delay="1.5">reversible</motion-ring>.
</p>Loop
Add loop to draw, hold, undraw and draw again. hold sets how long the highlight stays on
the text, gap how long it stays away. Add pause-on-hover to freeze it while the reader
points at it. The highlight erases from its start, so it leaves at its end.
Makes
<motion-marker loop hold="2" pause-on-hover>the code</motion-marker>Properties
| Attribute | Property | Type | Default | Description |
|---|---|---|---|---|
| | | string | 'view' | What draws the mark: |
| | | number | 0.6 | Spring duration of the drawing, in seconds |
| | | number | 0 | Seconds to wait before drawing. Use it to draw several marks in one sentence one after another |
| | | number | 0 | Spring bounciness. |
| | | boolean | false | Draw, hold, undraw and draw again on repeat. With |
| | | number | 1.6 | With |
| | | number | 0.5 | With |
| | | boolean | false | Pause the loop while the pointer is over the mark, resuming on leave |
| | | boolean | true | With |
| | | number | 0.6 | Share of the element, from 0 to 1, that must be visible before it draws |
| | | string | 'line' | |
CSS custom properties
| Property | Type | Default | Description |
|---|---|---|---|
| | color | rgb(250 204 21 / 0.4) | Color of the highlighter stroke |
JavaScript API
The mark has the shared playback API: play() draws it, cancel() removes it, finish() shows it fully drawn and replay() draws it again from the start. It fires motion-start, motion-finish and motion-cancel. With trigger="hover", each draw and undraw is a playback run too, so it fires motion-start and motion-finish. See the JavaScript API.
sweep() runs a fully drawn mark across once: it erases from its start, so it leaves at its end, then draws in again. It is a playback run, so it fires motion-start and motion-finish, and pause(), finish() and cancel() act on it. It does nothing under reduced motion or while the mark is not fully drawn.
Accessibility
The mark is decoration: the text stays real, selectable and read by screen readers exactly as written. If the highlight carries meaning, wrap the component in <mark>. Respects prefers-reduced-motion: the mark appears fully drawn at once, with no animation, and loop never starts.