Caret Swap
A gradient caret expands into a block that consumes the first text, then types the second
Installation
$ pnpm dlx shadcn@latest add @remocn/caret-swapUsage
The first text sits still until swapAt. Seven frames earlier the caret pops in after the last character; at swapAt it swells leftward into a full block with a sharp acceleration, consuming the text right to left in six frames. The block then dissolves its gradient tail to the right — the left edge holds still and what remains is the caret line at the start of the row. After a beat it types the second text — typeSpeed frames for the first character with every following gap shrinking, one accelerando to the finish — growing rightward from the anchor while drifting left to end centered. After the last character the caret blinks. All timings measured frame-by-frame from the reference footage.
// src/Root.tsx
import { Composition } from "remotion";
import { CaretSwap } from "@/components/remocn/caret-swap";
const CaretSwapScene = () => (
<CaretSwap
fromText="Executing it?"
toText="That's where everyone quits"
/>
);
export const RemotionRoot = () => (
<Composition
id="CaretSwap"
component={CaretSwapScene}
durationInFrames={100}
fps={30}
width={1280}
height={720}
/>
);With Backdrop
The component renders transparent — supply the background via Backdrop. It ships single-theme; edit the copied file to re-theme colors.
import { Composition } from "remotion";
import { CaretSwap } from "@/components/remocn/caret-swap";
import { Backdrop } from "@/components/remocn/backdrop";
const CaretSwapScene = () => (
<Backdrop fill={{ type: "color", value: "#fdf6f0" }}>
<CaretSwap
fromText="Executing it?"
toText="That's where everyone quits"
/>
</Backdrop>
);
export const RemotionRoot = () => (
<Composition
id="CaretSwap"
component={CaretSwapScene}
durationInFrames={100}
fps={30}
width={1280}
height={720}
/>
);Syncing
The exported caretSwapLength(toText, swapAt, typeSpeed) helper returns the frame on which the last character lands — use it as the lower bound of the Sequence, plus whatever hold you want for the blinking caret.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
fromTextrequired | string | — | The phrase shown first and consumed by the caret block |
toTextrequired | string | — | The phrase typed out after the swap |
fontSize | number | 72 | Font size in pixels; caret and block scale with it |
swapAt | number | 20 | Frame at which the caret starts expanding into the block; the caret itself appears seven frames earlier |
typeSpeed | number | 1 | Frames for the first typed character; every next gap shrinks by a fixed ratio so the typing accelerates toward the end |
color | string | "#171717" | Text color (any valid CSS color) |
caretColor | string | "#FF4DAA" | Top color of the caret gradient |
caretColorEnd | string | "#8A5CF6" | Bottom color of the caret gradient |
fontWeight | number | 400 | CSS font-weight |
className | string | — | Optional className passed to the underlying span |