Highlighter
Free · MITDraws a marker highlight, hand-drawn underline, circle, box or strike-through behind a phrase when it scrolls into view, in six colors.
Installation
$ npx shadcn@latest add https://goodui.dev/r/highlighter.jsonThe file lands in components/goodui/highlighter.tsx and is yours to edit. With the @goodui registry added, npx shadcn@latest add @goodui/highlighter works too.
Usage
import { Highlighter } from "@/components/goodui/highlighter"
export function Benefit() {
return (
<p className="max-w-xl text-lg text-muted-foreground">
Automatic SMS reminders{" "}
<Highlighter className="text-foreground">cut no-shows by 40%</Highlighter>
{" "}for salons in their first month, and clients can{" "}
<Highlighter action="underline" color="sky" delay={0.4}>reschedule in one tap</Highlighter>.
</p>
)
}When to use
- Drawing attention to the key phrase of a landing-page paragraph as it scrolls into view ("cut no-shows by 40%").
- Pricing and comparison copy: circle the saving, strike the old price.
- Onboarding and docs, to point at the one thing to notice, controlled by your own state.
- A highlighted word inside a heading: heading with treatment="highlight" or "underline-squiggle" and *marked* words is simpler.
- Search-result matches in lists and tables: use a plain <mark>; animation there is distracting.
- Coloring a word without a mark: use gradient-text or text with a tone.
| If you need | Use |
|---|---|
| Marker behind a phrase (wraps across lines) | action="highlight" (default) |
| Hand-drawn underline | action="underline" |
| Circle a number or price | action="circle" |
| Box a term | action="box" |
| Old price | action="strike" color="rose" |
| Follow the theme color | color="primary" |
| Draw from your own state | show={step === 2} |
Examples
Old and new price
<p className="text-2xl font-semibold">
<Highlighter action="strike" color="rose">$49</Highlighter>{" "}
<Highlighter action="circle" color="emerald" delay={0.5} padding={8}>$29</Highlighter>
<span className="text-base text-muted-foreground"> / month</span>
</p>Controlled by an onboarding step
<Highlighter action="box" color="primary" show={step === 2}>Connect your calendar</Highlighter>API reference
| Prop | Type | Default |
|---|---|---|
| action Marker highlight (wraps across lines), or a hand-drawn underline, circle, box or strike-through. | "highlight" | "underline" | "circle" | "box" | "strike" | "highlight" |
| color Marker and stroke color, tuned for light and dark; "primary" follows the theme. | "amber" | "emerald" | "sky" | "rose" | "violet" | "primary" | "amber" |
| show Controlled: true draws, false erases. Leave undefined to draw when scrolled into view. | boolean | — |
| once With automatic triggering: draw only the first time it enters the viewport. | boolean | true |
| delay Seconds before drawing starts. | number | 0 |
| duration Seconds the stroke takes. | number | 0.8 |
| strokeWidth Stroke width in px for underline, circle, box and strike (not used by highlight). | number | 2 |
| padding Extra space in px between the text and a circle or box. | number | 6 |
Also accepts every <span> attribute.
Accessibility
- The text stays plain text in a <span>; the drawn shapes are an aria-hidden SVG with pointer-events: none.
- The marker is decoration: if the emphasis carries meaning, also use <strong> or <em> inside it.
- Each color has separate light and dark values; the dark-mode marker is a translucent tint (about 30% opacity) that light text shows through.
Motion
- highlight: the marker grows from left to right by animating background-size over `duration` s (0.8) on cubic-bezier(0.65, 0, 0.35, 1), after `delay` s. It wraps across lines.
- underline, circle, box, strike: an SVG path built from the measured text size draws itself (pathLength) with the same duration, delay and easing.
- Without show, it draws when 80% of the phrase is visible, once by default (once={false} erases and redraws on each visit).
- Reduced motion: marks appear at once, without drawing.
Notes for AI
Coding agents read these rules through MCP and llms.txt, or paste this page with Copy page.
- Wrap only the phrase to mark; children are the text.
- Durations and delay are seconds. strokeWidth and padding (px) only affect the SVG actions, not highlight.
- Colors are amber, emerald, sky, rose, violet and primary; there is no custom color prop.
- underline, circle, box and strike turn the span into an inline-block, so keep those phrases short enough to stay on one line; use highlight for phrases that wrap.
- Stagger several highlights with delay rather than separate timers.
FAQ
Does the marker work across line breaks?
Yes, action="highlight" is a CSS background on the inline text, so it follows every line. The SVG actions make the phrase an inline-block, so they belong on short phrases.
Does it redraw when the text resizes?
Yes. The SVG actions re-measure with a ResizeObserver, so shapes keep fitting after font loads and layout changes.
Related
Layered heading: semantic level and size decoupled, sans/serif/mono/display font slots you map to your own fonts, eight ink treatments (gradient, muted tail, outline, marker highlight, hand-drawn squiggle, shimmer…), serif-italic emphasis via _words_, and on-view word, letter and mask reveals.
Text filled with a slowly drifting multi-stop gradient, with Aurora, Sunset, Ocean and theme palettes tuned separately for light and dark.
Reveals a heading word by word or letter by letter with a staggered blur, fade and rise when it scrolls into view or on demand.
Every body-copy role in one primitive: paragraph, lead, small, caption, overline, eyebrow pill, quote with attribution and inline code, with tones, readable measures, truncate or line clamp, and on-view entrances.