packages/docs/docs/text-highlights.mdx
We recommend @remotion/rough-notation to annotate text to add marker highlights, encircle words, box or strike through parts.
Use <Highlight> from @remotion/rough-notation for a hand-drawn highlight behind text.
import {loadFont} from '@remotion/google-fonts/CormorantGaramond';
import {Highlight} from '@remotion/rough-notation';
import React from 'react';
import {
AbsoluteFill,
Easing,
Interactive,
interpolate,
useCurrentFrame,
} from 'remotion';
const {fontFamily} = loadFont('normal', {
weights: ['700'],
subsets: ['latin'],
});
const containerStyle: React.CSSProperties = {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
};
export const RoughNotationHighlight: React.FC = () => {
const frame = useCurrentFrame();
return (
<AbsoluteFill style={containerStyle}>
<Interactive.Div
style={{
fontSize: 80,
fontWeight: 700,
lineHeight: 1.1,
color: '#171717',
fontFamily,
width: 800,
}}
>
<Interactive.Span>A truly </Interactive.Span>
<Highlight
name="Highlight annotation"
progress={interpolate(frame, [0, 25], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: [
Easing.spring({
damping: 200,
mass: 1,
stiffness: 100,
allowTail: true,
durationRestThreshold: 0.02,
overshootClamping: false,
}),
],
})}
color={'rgba(255, 236, 79, 0.62)'}
maxRandomnessOffset={10}
roughness={2.3}
padding={{
left: 20,
right: 20,
}}
bowing={0}
>
remarkable
</Highlight>{' '}
<Interactive.Span>end to the World cup</Interactive.Span>
</Interactive.Div>
</AbsoluteFill>
);
};
Use <Underline> to add a drawn underline.
import {loadFont} from '@remotion/google-fonts/CormorantGaramond';
import {Underline} from '@remotion/rough-notation';
import React from 'react';
import {
AbsoluteFill,
Interactive,
interpolate,
useCurrentFrame,
} from 'remotion';
const {fontFamily} = loadFont('normal', {
weights: ['700'],
subsets: ['latin'],
});
const containerStyle: React.CSSProperties = {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
};
export const RoughNotationUnderline: React.FC = () => {
const frame = useCurrentFrame();
return (
<AbsoluteFill style={containerStyle}>
<Interactive.Div
style={{
fontSize: 80,
fontWeight: 700,
lineHeight: 1.1,
color: '#171717',
fontFamily,
width: 800,
}}
>
<Interactive.Span>Underline </Interactive.Span>
<Underline
name="Underline annotation"
progress={interpolate(frame, [14, 35], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
})}
color={'rgba(53, 95, 143, 0.71)'}
strokeWidth={10}
iterations={3}
>
this
</Underline>{' '}
<Interactive.Span>phrase</Interactive.Span>
</Interactive.Div>
</AbsoluteFill>
);
};
Use <StrikeThrough> to cross out a word with a single line.
import {loadFont} from '@remotion/google-fonts/CormorantGaramond';
import {StrikeThrough} from '@remotion/rough-notation';
import React from 'react';
import {
AbsoluteFill,
Interactive,
interpolate,
useCurrentFrame,
} from 'remotion';
const {fontFamily} = loadFont('normal', {
weights: ['700'],
subsets: ['latin'],
});
const containerStyle: React.CSSProperties = {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
};
export const RoughNotationStrikeThrough: React.FC = () => {
const frame = useCurrentFrame();
return (
<AbsoluteFill style={containerStyle}>
<Interactive.Div
style={{
fontSize: 80,
fontWeight: 700,
lineHeight: 1.1,
color: '#171717',
fontFamily,
width: 800,
}}
>
<Interactive.Span>The </Interactive.Span>
<StrikeThrough
name="Strike-through annotation"
progress={interpolate(frame, [10, 25], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
})}
color={'#f11515'}
strokeWidth={14}
>
forbidden
</StrikeThrough>{' '}
<Interactive.Span>fruit</Interactive.Span>
</Interactive.Div>
</AbsoluteFill>
);
};
Use <CrossedOff> for a stronger removal mark.
import {loadFont} from '@remotion/google-fonts/CormorantGaramond';
import {CrossedOff} from '@remotion/rough-notation';
import React from 'react';
import {
AbsoluteFill,
Interactive,
interpolate,
useCurrentFrame,
} from 'remotion';
const {fontFamily} = loadFont('normal', {
weights: ['700'],
subsets: ['latin'],
});
const containerStyle: React.CSSProperties = {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
};
export const RoughNotationCrossedOff: React.FC = () => {
const frame = useCurrentFrame();
return (
<AbsoluteFill style={containerStyle}>
<Interactive.Div
style={{
fontSize: 80,
fontWeight: 700,
lineHeight: 1.1,
color: '#171717',
fontFamily,
width: 800,
}}
>
<Interactive.Span>Please </Interactive.Span>
<CrossedOff
name="Crossed off annotation"
progress={interpolate(frame, [18, 39], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
})}
color={'#eb2525'}
strokeWidth={6}
iterations={10}
roughness={2}
>
remove
</CrossedOff>{' '}
<Interactive.Span>this</Interactive.Span>
</Interactive.Div>
</AbsoluteFill>
);
};
Use <Box> to draw a rectangle around text.
import {loadFont} from '@remotion/google-fonts/CormorantGaramond';
import {Box} from '@remotion/rough-notation';
import React from 'react';
import {
AbsoluteFill,
Interactive,
interpolate,
useCurrentFrame,
} from 'remotion';
const {fontFamily} = loadFont('normal', {
weights: ['700'],
subsets: ['latin'],
});
const containerStyle: React.CSSProperties = {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
};
export const RoughNotationBox: React.FC = () => {
const frame = useCurrentFrame();
return (
<AbsoluteFill style={containerStyle}>
<Interactive.Div
style={{
fontSize: 80,
fontWeight: 700,
lineHeight: 1.1,
color: '#171717',
fontFamily,
width: 800,
textAlign: 'center',
}}
>
<Interactive.Span>Think outside the </Interactive.Span>
<Box
name="Box annotation"
progress={interpolate(frame, [0, 23], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
})}
color={'#fe0000'}
strokeWidth={8}
iterations={1}
padding={{
left: 3,
right: 6,
top: -12,
bottom: -5,
}}
roughness={0.9}
>
box
</Box>
</Interactive.Div>
</AbsoluteFill>
);
};
Use <Bracket> with individual bracket flags to draw selected sides.
import {loadFont} from '@remotion/google-fonts/CormorantGaramond';
import {Bracket} from '@remotion/rough-notation';
import React from 'react';
import {
AbsoluteFill,
Interactive,
interpolate,
useCurrentFrame,
} from 'remotion';
const {fontFamily} = loadFont('normal', {
weights: ['700'],
subsets: ['latin'],
});
const containerStyle: React.CSSProperties = {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
};
export const RoughNotationBracket: React.FC = () => {
const frame = useCurrentFrame();
return (
<AbsoluteFill style={containerStyle}>
<Interactive.Div
style={{
fontSize: 80,
fontWeight: 700,
lineHeight: 1.1,
color: '#171717',
fontFamily,
width: 800,
}}
>
<Interactive.Span>Mark </Interactive.Span>
<Bracket
name="Bracket annotation"
progress={interpolate(frame, [0, 60], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
})}
bracketLeft
bracketRight
bracketTop
roughness={1}
bowing={3}
strokeWidth={8}
color="#dc2626"
bracketBottom
padding={{
top: -11,
}}
>
this
</Bracket>{' '}
<Interactive.Span>part</Interactive.Span>
</Interactive.Div>
</AbsoluteFill>
);
};
Use <Circle> when the annotation should sit above the text.
import {loadFont} from '@remotion/google-fonts/CormorantGaramond';
import {Circle} from '@remotion/rough-notation';
import React from 'react';
import {
AbsoluteFill,
Easing,
Interactive,
interpolate,
useCurrentFrame,
} from 'remotion';
const {fontFamily} = loadFont('normal', {
weights: ['700'],
subsets: ['latin'],
});
const containerStyle: React.CSSProperties = {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
};
export const RoughNotationCircle: React.FC = () => {
const frame = useCurrentFrame();
return (
<AbsoluteFill style={containerStyle}>
<Interactive.Div
style={{
fontSize: 80,
fontWeight: 700,
lineHeight: 1.1,
color: '#171717',
fontFamily,
width: 800,
}}
>
<Interactive.Span>How much </Interactive.Span>
<Circle
name="Circle annotation"
progress={interpolate(frame, [0, 43], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: [Easing.bezier(0.42, 0, 0.58, 1)],
})}
roughness={1.8}
strokeWidth={12}
color={'rgba(37, 99, 235, 0.57)'}
padding={{
left: 10,
right: 10,
top: 10,
bottom: 10,
}}
box={'inside'}
>
circular
</Circle>{' '}
<Interactive.Span>financing is in AI?</Interactive.Span>
</Interactive.Div>
</AbsoluteFill>
);
};
Animate seed to redraw an annotation on every frame.
import {loadFont} from '@remotion/google-fonts/CormorantGaramond';
import {Circle} from '@remotion/rough-notation';
import React from 'react';
import {
AbsoluteFill,
Interactive,
interpolate,
useCurrentFrame,
} from 'remotion';
const {fontFamily} = loadFont('normal', {
weights: ['700'],
subsets: ['latin'],
});
const containerStyle: React.CSSProperties = {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
};
export const RoughNotationAnimatedSeed: React.FC = () => {
const frame = useCurrentFrame();
return (
<AbsoluteFill style={containerStyle}>
<Interactive.Div
style={{
fontSize: 80,
fontWeight: 700,
lineHeight: 1.1,
color: '#171717',
fontFamily,
width: 800,
textAlign: 'center',
}}
>
<Circle
name="Animated seed annotation"
progress={1}
color={'#2563eb'}
strokeWidth={10}
roughness={1.8}
seed={interpolate(frame, [0, 89], [1, 90], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
})}
>
motion
</Circle>
</Interactive.Div>
</AbsoluteFill>
);
};
Add posterize to interpolate to lower the frame rate of the seed change.
import {loadFont} from '@remotion/google-fonts/CormorantGaramond';
import {Circle} from '@remotion/rough-notation';
import React from 'react';
import {
AbsoluteFill,
Interactive,
interpolate,
useCurrentFrame,
} from 'remotion';
const {fontFamily} = loadFont('normal', {
weights: ['700'],
subsets: ['latin'],
});
const containerStyle: React.CSSProperties = {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
};
export const RoughNotationPosterizedSeed: React.FC = () => {
const frame = useCurrentFrame();
return (
<AbsoluteFill style={containerStyle}>
<Interactive.Div
style={{
fontSize: 80,
fontWeight: 700,
lineHeight: 1.1,
color: '#171717',
fontFamily,
width: 800,
textAlign: 'center',
}}
>
<Circle
name="Posterized seed annotation"
progress={1}
color={'#2563eb'}
strokeWidth={10}
roughness={1.8}
seed={interpolate(frame, [0, 89], [1, 90], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
posterize: 10,
})}
>
motion
</Circle>
</Interactive.Div>
</AbsoluteFill>
);
};
Apply posterize to progress to make the circle draw in discrete steps.
import {loadFont} from '@remotion/google-fonts/CormorantGaramond';
import {Circle} from '@remotion/rough-notation';
import React from 'react';
import {
AbsoluteFill,
Interactive,
interpolate,
useCurrentFrame,
} from 'remotion';
const {fontFamily} = loadFont('normal', {
weights: ['700'],
subsets: ['latin'],
});
const containerStyle: React.CSSProperties = {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
};
export const RoughNotationPosterizedProgress: React.FC = () => {
const frame = useCurrentFrame();
return (
<AbsoluteFill style={containerStyle}>
<Interactive.Div
style={{
fontSize: 80,
fontWeight: 700,
lineHeight: 1.1,
color: '#171717',
fontFamily,
width: 800,
textAlign: 'center',
}}
>
<Circle
name="Posterized progress annotation"
progress={interpolate(frame, [0, 43], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
posterize: 10,
})}
color={'#2563eb'}
strokeWidth={10}
roughness={1.8}
iterations={1}
>
motion
</Circle>
</Interactive.Div>
</AbsoluteFill>
);
};