Fonttrio — Three fonts. One command.
Install this video
Adds the full composition, its remocn dependencies, and the prompt to your Remotion project via the shadcn registry.
$ pnpm dlx shadcn@latest add kapishdima/remocn-demo/fonttrioRender it locally
Renders the MP4 on your machine with the Remotion CLI.
$ pnpm dlx remotion render fonttrio out/fonttrio.mp4 --scale=2 --crf=15 --x264-preset=slower --jpeg-quality=95The prompt
Reconstructed draftA reconstruction of the prompt this video was generated from.
Make a type-forward spot for Fonttrio, our curated font-pairing registry for shadcn/ui. Open with a few pain questions about picking fonts, land a punchy "Three fonts." hook, then reveal the editorial pairing — headline, body, and mono roles — each one actually set in its real typeface (Playfair Display, Source Serif 4, JetBrains Mono) so people can see the pairing, not just hear about it. Show the install command zooming in (npx shadcn add @fonttrio/editorial), pop open a glass code block with the generated CSS variables, roll up a count of the curated pairings (78), and do a specimen wall of "Ag" across a few different moods like editorial, bold, impact, clean. Close with the trio mark, the Fonttrio wordmark set in Playfair Display, the tagline, and a copy-ready install pill. Use remocn for the typewriter and glass code block pieces.
The code
The exact source the AI wrote — the same files the install command puts in your project.
import React, { type CSSProperties, type ReactNode } from "react";
import { AbsoluteFill, Easing, Series, interpolate, interpolateColors, spring, useCurrentFrame, useVideoConfig } from "remotion";
import { demoAsset } from "@/lib/demo-assets";
import {
TransitionSeries,
linearTiming,
type TransitionPresentation,
type TransitionPresentationComponentProps,
} from "@remotion/transitions";
import { loadFont as loadSans } from "@remotion/google-fonts/Manrope";
import { loadFont as loadPlayfair } from "@remotion/google-fonts/PlayfairDisplay";
import { loadFont as loadSourceSerif } from "@remotion/google-fonts/SourceSerif4";
import { loadFont as loadMono } from "@remotion/google-fonts/JetBrainsMono";
import { loadFont as loadAbril } from "@remotion/google-fonts/AbrilFatface";
import { loadFont as loadBebas } from "@remotion/google-fonts/BebasNeue";
import { loadFont as loadUrbanist } from "@remotion/google-fonts/Urbanist";
import { RemocnUIProvider } from "@/lib/remocn-ui";
import { Backdrop } from "@/components/remocn/backdrop";
import { GlassCodeBlock } from "@/components/remocn/glass-code-block";
import { SharedAxisZ } from "@/components/remocn/shared-axis-z";
import { RollingNumber } from "@/components/remocn/rolling-number";
import { TerminalCursorZoom } from "@/components/remocn/terminal-cursor-zoom";
import { useBlurInTransition } from "@/components/remocn/use-blur-in-transition";
import { BlurIn } from "@/components/remocn/blur-in";
import { CheckIcon, CopyIcon } from "lucide-react";
// ---------------------------------------------------------------------------
// Fonts. The whole point of a type product is to show the real typefaces — so
// every specimen here renders in its actual family, not a stand-in.
// ---------------------------------------------------------------------------
const { fontFamily: SANS_FAMILY } = loadSans("normal", {
subsets: ["latin"],
weights: ["400", "500", "600", "700"],
});
const { fontFamily: PLAYFAIR_FAMILY } = loadPlayfair("normal", {
subsets: ["latin"],
weights: ["500", "700", "800"],
});
const { fontFamily: SOURCE_SERIF_FAMILY } = loadSourceSerif("normal", {
subsets: ["latin"],
weights: ["400", "600"],
});
const { fontFamily: MONO_FAMILY } = loadMono("normal", {
subsets: ["latin"],
weights: ["400", "700"],
});
const { fontFamily: ABRIL_FAMILY } = loadAbril("normal", {
subsets: ["latin"],
weights: ["400"],
});
const { fontFamily: BEBAS_FAMILY } = loadBebas("normal", {
subsets: ["latin"],
weights: ["400"],
});
const { fontFamily: URBANIST_FAMILY } = loadUrbanist("normal", {
subsets: ["latin"],
weights: ["600", "800"],
});
const SANS = `${SANS_FAMILY}, -apple-system, BlinkMacSystemFont, sans-serif`;
const MONO = `${MONO_FAMILY}, ui-monospace, SFMono-Regular, monospace`;
const PLAYFAIR = `${PLAYFAIR_FAMILY}, Georgia, serif`;
const SOURCE_SERIF = `${SOURCE_SERIF_FAMILY}, Georgia, serif`;
const ABRIL = `${ABRIL_FAMILY}, Georgia, serif`;
const BEBAS = `${BEBAS_FAMILY}, Impact, sans-serif`;
const URBANIST = `${URBANIST_FAMILY}, sans-serif`;
// Editorial palette — warm ink + a single restrained gold accent.
const GOLD = "#e0a23c";
const INK = "#fafafa";
const MUTED = "rgba(250,250,250,0.62)";
const FAINT = "rgba(250,250,250,0.4)";
// ---------------------------------------------------------------------------
// Scene timings (frames @ 30fps), one per beat. Transitions overlap.
// ---------------------------------------------------------------------------
const S_INTRO = 140; // three pain questions via shared-axis-z
const S_THREE = 48; // "Three fonts." — the hook
const S_TRIO = 140; // the editorial pairing — three real specimens
const S_INSTALL = 82; // terminal-cursor-zoom install
const S_CODE = 132; // generated CSS variables (GlassCodeBlock)
const S_COUNT = 88; // 78 curated pairings (rolling-number)
const S_WALL = 112; // specimen wall across moods
const S_OUTRO = 112; // wordmark + tagline + install pill
const T_ZOOM = 18; // intro → three (punch in)
const T_X = 14; // generic crossfade
const T_BLUR = 16; // trio → install (background clash)
const T_OUT = 20; // wall → outro
export const FONTTRIO_DURATION =
S_INTRO +
S_THREE +
S_TRIO +
S_INSTALL +
S_CODE +
S_COUNT +
S_WALL +
S_OUTRO -
(T_ZOOM + T_X + T_BLUR + T_X + T_X + T_X + T_OUT);
// ---------------------------------------------------------------------------
// Reveal — blur-in wrapper driven by useBlurInTransition.
// ---------------------------------------------------------------------------
const Reveal: React.FC<{
children: ReactNode;
delay?: number;
distance?: number;
blur?: number;
duration?: number;
display?: CSSProperties["display"];
}> = ({
children,
delay = 0,
distance = 16,
blur = 10,
duration = 20,
display = "block",
}) => {
const style = useBlurInTransition(
[{ at: delay, state: "revealed", duration }],
{ direction: "up", distance, blur },
);
return (
<BlurIn style={style} display={display}>
{children}
</BlurIn>
);
};
// ---------------------------------------------------------------------------
// Mark — inline marker swipe behind a phrase.
// ---------------------------------------------------------------------------
const Mark: React.FC<{
children: string;
color?: string;
startFrame?: number;
}> = ({ children, color = GOLD, startFrame = 8 }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const scaleX = spring({
frame: frame - startFrame,
fps,
config: { damping: 15, mass: 0.8 },
});
const textColor = interpolateColors(
interpolate(scaleX, [0.45, 0.85], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
}),
[0, 1],
[INK, "#0a0a0a"],
);
return (
<span
style={{
position: "relative",
display: "inline-block",
whiteSpace: "nowrap",
}}
>
<span
aria-hidden
style={{
position: "absolute",
inset: "-0.02em -0.12em",
background: color,
borderRadius: 6,
transformOrigin: "left center",
transform: `scaleX(${scaleX})`,
zIndex: 0,
}}
/>
<span style={{ position: "relative", zIndex: 1, color: textColor }}>
{children}
</span>
</span>
);
};
// Small "trio" mark — three type-weight bars, the middle one gold.
const TrioMark: React.FC<{ size?: number }> = ({ size = 60 }) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none">
<rect x={3} y={4} width={3.4} height={16} rx={1.7} fill={INK} />
<rect x={10.3} y={4} width={3.4} height={16} rx={1.7} fill={GOLD} />
<rect x={17.6} y={4} width={3.4} height={16} rx={1.7} fill={INK} />
</svg>
);
// ===========================================================================
// Scene 1 — Intro. Three setup-the-pain questions, each via shared-axis-z.
// ===========================================================================
const Q1 = "Hand-picking fonts for every project?";
const Q2 = "Wiring CSS variables by hand?";
const Q3 = "Rebuilding the type scale each time?";
const IntroScene: React.FC = () => (
<AbsoluteFill style={{ padding: "0 90px" }}>
<Series>
<Series.Sequence durationInFrames={42} layout="none">
<SharedAxisZ
fromText=""
toText={Q1}
fontSize={42}
fontWeight={600}
color={"#FFFFFF"}
/>
</Series.Sequence>
<Series.Sequence durationInFrames={46} layout="none">
<SharedAxisZ
fromText={Q1}
toText={Q2}
fontSize={42}
fontWeight={600}
color={"#FFFFFF"}
/>
</Series.Sequence>
<Series.Sequence durationInFrames={52} layout="none">
<SharedAxisZ
fromText={Q2}
toText={Q3}
fontSize={42}
fontWeight={600}
color={"#FFFFFF"}
/>
</Series.Sequence>
</Series>
</AbsoluteFill>
);
// ===========================================================================
// Scene 2 — The hook. "Three fonts." settles hard and centered.
// ===========================================================================
const ThreeScene: React.FC = () => (
<AbsoluteFill style={{ alignItems: "center", justifyContent: "center" }}>
<SharedAxisZ
fromText={""}
toText={"Three fonts."}
fontSize={130}
fontWeight={700}
color={"#FFFFFF"}
/>
</AbsoluteFill>
);
// ===========================================================================
// Scene 3 — The editorial pairing. One pairing, three roles — each rendered in
// its real typeface so the viewer reads the actual fonts.
// ===========================================================================
const TrioRow: React.FC<{
role: string;
name: string;
family: string;
cssVar: string;
nameSize: number;
delay: number;
divider?: boolean;
}> = ({ role, name, family, cssVar, nameSize, delay, divider = true }) => (
<Reveal delay={delay} distance={22} blur={10} duration={20}>
<div
style={{
display: "flex",
alignItems: "flex-end",
justifyContent: "space-between",
gap: 24,
padding: "20px 0",
borderTop: divider ? "1px solid rgba(250,250,250,0.1)" : "none",
}}
>
<div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
<span
style={{
fontFamily: SANS,
fontSize: 14,
fontWeight: 600,
color: GOLD,
}}
>
{role}
</span>
<span
style={{
fontFamily: family,
fontSize: nameSize,
color: INK,
lineHeight: 1,
}}
>
{name}
</span>
</div>
<span style={{ fontFamily: MONO, fontSize: 15, color: FAINT }}>
{cssVar}
</span>
</div>
</Reveal>
);
const TrioScene: React.FC = () => (
<AbsoluteFill
style={{
alignItems: "center",
justifyContent: "center",
flexDirection: "column",
gap: 26,
}}
>
<Reveal delay={2} distance={16} blur={10}>
<div
style={{ display: "flex", alignItems: "center", gap: 14 }}
>
<span
style={{
fontFamily: MONO,
fontSize: 14,
color: GOLD,
padding: "5px 12px",
borderRadius: 999,
border: `1px solid ${GOLD}55`,
background: `${GOLD}1a`,
}}
>
editorial
</span>
<h2
style={{
margin: 0,
fontFamily: SANS,
fontWeight: 600,
fontSize: 34,
color: INK,
}}
>
One pairing, three roles
</h2>
</div>
</Reveal>
<Reveal delay={8} distance={36} blur={14} duration={22}>
<div
style={{
width: 740,
padding: "12px 40px 24px",
borderRadius: 20,
background: "rgba(10,10,10,0.55)",
border: `1px solid ${GOLD}3a`,
boxShadow: "0 20px 50px rgba(0,0,0,0.45)",
}}
>
<TrioRow
role="Heading"
name="Playfair Display"
family={PLAYFAIR}
cssVar="--font-heading"
nameSize={46}
delay={16}
divider={false}
/>
<TrioRow
role="Body"
name="Source Serif 4"
family={SOURCE_SERIF}
cssVar="--font-body"
nameSize={38}
delay={24}
/>
<TrioRow
role="Mono"
name="JetBrains Mono"
family={MONO}
cssVar="--font-mono"
nameSize={30}
delay={32}
/>
</div>
</Reveal>
</AbsoluteFill>
);
// ===========================================================================
// Scene 4 — Install. terminal-cursor-zoom dollies across the typed command.
// ===========================================================================
const InstallScene: React.FC = () => (
<AbsoluteFill>
<TerminalCursorZoom
command="npx shadcn add @fonttrio/editorial"
title="~/my-app"
fontSize={22}
zoom={2.4}
charsPerFrame={1}
/>
</AbsoluteFill>
);
// ===========================================================================
// Scene 5 — What lands in your project: the generated CSS variables.
// ===========================================================================
const CSS_EXAMPLE = `/* app/globals.css — added by Fonttrio */
:root {
--font-heading: var(--font-playfair-display);Showing the first 400 of 848 lines. View the full file on GitHub.