text-emphasis
remotionui/text-emphasisFreeUtility
Word-level phrase matching and marker geometry for scenes that sweep emphasis across text
Install it
npx shadcn@latest add https://remotionui.com/r/text-emphasis.jsonSource
1/**2 * Word-level emphasis matching for scenes that sweep a marker across a phrase.3 *4 * Splitting to words — rather than wrapping the phrase in one span — keeps the5 * marker tracking the text once the line wraps, and lets each word be swept in6 * turn so the highlight reads as a stroke instead of a block appearing at once.7 */89export type EmphasisWord = {10 /** The word as it appears in the source string. */11 text: string;12 /** True when the word falls inside the emphasised phrase. */13 marked: boolean;14 /** Index of this word among the marked ones, for staggering. Otherwise -1. */15 order: number;16};1718/**19 * Splits `text` into words, flagging the ones covered by `phrase`.20 * Matching is case-insensitive; an absent or unmatched phrase marks nothing.21 */22export function markEmphasis(text: string, phrase?: string): EmphasisWord[] {23 const words = text.split(/\s+/).filter(Boolean);24 const plain = () =>25 words.map((word) => ({ text: word, marked: false, order: -1 }));2627 if (!phrase || !phrase.trim()) return plain();2829 const needle = phrase.trim().toLowerCase();30 const start = text.toLowerCase().indexOf(needle);31 if (start < 0) return plain();3233 const end = start + needle.length;34 let cursor = 0;35 let order = 0;3637 return words.map((word) => {38 const at = text.indexOf(word, cursor);39 cursor = at + word.length;40 const marked = at < end && cursor > start;41 return { text: word, marked, order: marked ? order++ : -1 };42 });43}4445/**46 * Rounded corners and insets for a marker behind `words[index]`, bridging the47 * space to a neighbouring marked word so a phrase reads as one stroke.48 */49export function markerEdges(50 words: EmphasisWord[],51 index: number,52 gap: string,53 overhang = 5,54 radius = 5,55): {56 left: number;57 right: number | string;58 borderTopLeftRadius: number;59 borderBottomLeftRadius: number;60 borderTopRightRadius: number;61 borderBottomRightRadius: number;62} {63 const joinsLeft = index > 0 && words[index - 1].marked;64 const joinsRight = Boolean(words[index + 1]?.marked);6566 return {67 left: joinsLeft ? 0 : -overhang,68 right: joinsRight ? `-${gap}` : -overhang,69 borderTopLeftRadius: joinsLeft ? 0 : radius,70 borderBottomLeftRadius: joinsLeft ? 0 : radius,71 borderTopRightRadius: joinsRight ? 0 : radius,72 borderBottomRightRadius: joinsRight ? 0 : radius,73 };74}
More from remotionui
All 127 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| ai-composer-utilsai-composer-utils | remotionui | Utilities | Free | no deps | |
| audio-viz-utilsaudio-viz-utils | remotionui | Utilities | Free | no deps | |
| caption-utilscaption-utils | remotionui | Utilities | Free | no deps | |
| chart-utilschart-utils | remotionui | Utilities | Free | no deps | |
| code-syntaxcode-syntax | remotionui | Utilities | Free | no deps | |
| layoutlayout | remotionui | Utilities | Free | no deps | |
| map-utilsmap-utils | remotionui | Utilities | Free | no deps | |
| media-utilsmedia-utils | remotionui | Utilities | Free | no deps |
