shadcn.io is not affiliated with official shadcn/ui
Annotation Text marks a word or a short phrase by hand: an underline, a box, a circle, a strike-through or a pair of brackets, drawn along its length as if a pen were pulling it across the page. Each mark wobbles a little, the way a hand does, and the wobble comes from the text itself, so the same phrase is always drawn the same way.
Use it for the word a reader should not miss in a headline, a before-and-after in a sentence, or a correction in a blog post. To lay a highlighter's band of colour behind a phrase instead, use Highlight Text; to mark every match of a search, Search Highlight Text. The mark is an SVG stroke laid over the words and drawn by a CSS keyframe animation the install adds to your stylesheet, with no animation library. With reduced motion the mark is simply there.
Installation
Usage
import { AnnotationText } from "@/components/ui/annotation-text";<h2>
Ship it <AnnotationText text="today" type="circle" />.
</h2>Demos
Types
type is the mark: underline, the default, box, circle, strike and bracket. A strike-through puts the words in an <s>; the other marks put them in a <mark>.
Color
markColor takes any CSS colour, such as a theme variable: var(--destructive) for a red pen, var(--info) for a blue one. delay holds a mark back, so a correction can follow the word it replaces.
Hover
trigger="hover" draws the mark while the pointer is over the words and takes it back as the pointer leaves; duration is how long one stroke takes, here 500 ms. The default, view, draws it once the words scroll into view, and mount as soon as the page shows them.
Accessibility
The words stay real text, read once by screen readers and selectable; the mark is decoration, hidden from them. When the mark carries meaning, the element says it too: a strike-through is an <s>, the other marks a <mark>, which some screen readers announce as highlighted. In forced-colors mode the stroke takes the system's text colour. With reduced motion the mark is drawn from the start.
FAQ
How does the mark fit the words without measuring them?
The mark is drawn in its own small drawing space, an em high, laid over the words and stretched to their exact width. The words' width is only estimated from their characters, so the stretch is slight: at worst the pen strokes running up and down come out a touch thicker or thinner than the ones running across.
Why does each mark look a little different?
Each one wobbles by a number worked out from its own text, never drawn at random, so a phrase is marked the same way on every visit and on the server, and two different phrases rarely look alike.
Can it mark a long phrase?
A word or a few. The phrase stays on one line so the mark can go round it; a phrase too long for its line wraps inside the mark, which then circles or boxes both lines.
How do I mark several words in one sentence?
Use one Annotation Text per word, each with its own type, and delay them in reading order: delay={0} for the first, delay={900} for the next. The Color demo does it with a strike-through followed by an underline.
Does it follow the font size?
Yes. The mark, its stroke and its distance from the words are all in ems, so it scales with the text around it, from a label to a hero.
API Reference
AnnotationText
<span> data-slot="annotation-text"
textstring
The words the mark goes round, under or through: a word or a short phrase.
delay?number
The wait before the mark starts, in milliseconds. @default 200.
duration?number
How long the mark takes to draw, in milliseconds. @default 800.
markColor?string
The ink's colour, any CSS colour. @default "var(--primary)".
trigger?"mount" | "view" | "hover"
When it draws: once the text scrolls into view, as soon as it's on the page, or while the pointer is over it (and back as it leaves).
- Type
"mount" | "view" | "hover"- Default
"view"
type?Mark
The mark. @default "underline".
- Default
"underline"
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.
data-view?string
"in" when visible is true, otherwise "out".
--annotation-text-colorstring
The ink's colour.
--annotation-text-delaystring
The wait before the mark starts (200ms).
--annotation-text-durationstring
How long the mark takes to draw (800ms).
Related
Highlight Text
DecorativeA highlighter band behind a phrase
- Pro
Search Highlight Text
InteractiveMark every match of a search
- Pro
Drop Cap Text
EntranceOpen the article with a large initial
- Pro
Writing Text
EntranceA quote that arrives at reading pace
- Pro
Stamp Text
EntranceA word stamped on by hand
- Pro
Paint Text
InteractivePaint the letters with the pointer