← Catalog
No preview yet
Source
158 lines// Hero Title Block — big centered headline with a subtitle over an
// optional background video. Spring pop-in, slow zoom, fade-out.
// Good for the first 1–3 seconds of a clip to set context before the
// main content starts.
import {
AbsoluteFill,
OffthreadVideo,
interpolate,
spring,
useCurrentFrame,
useVideoConfig,
} from 'remotion';
import { loadFont as loadInter } from '@remotion/google-fonts/Inter';
import { loadFont as loadRoboto } from '@remotion/google-fonts/Roboto';
import { loadFont } from '@remotion/google-fonts/SpaceGrotesk';
const { fontFamily: SPACE_GROTESK } = loadFont();
const { fontFamily: INTER } = loadInter();
const { fontFamily: ROBOTO } = loadRoboto();
const FONT_MAP: Record<string, string> = {
SpaceGrotesk: SPACE_GROTESK,
Inter: INTER,
Roboto: ROBOTO,
};
export type HeroTitleProps = {
backgroundVideo: string;
title: string;
subtitle: string;
titleColor: string;
titleFontSize: number;
subtitleColor: string;
subtitleFontSize: number;
titleFont: 'SpaceGrotesk' | 'Inter' | 'Roboto';
verticalAlign: number;
backgroundColor: string;
overlayOpacity: number;
};
export default function HeroTitle({
backgroundVideo,
title = 'Breaking',
subtitle = '',
titleColor = '#FFFFFF',
titleFontSize = 140,
subtitleColor = '#FFD84D',
subtitleFontSize = 40,
titleFont = 'SpaceGrotesk',
verticalAlign = 50,
backgroundColor = '#05060a',
overlayOpacity = 0.45,
}: Partial<HeroTitleProps>) {
const frame = useCurrentFrame();
const { fps, durationInFrames } = useVideoConfig();
const titleFamily = FONT_MAP[titleFont] ?? SPACE_GROTESK;
const hasVideo =
typeof backgroundVideo === 'string' &&
/^https?:\/\//i.test(backgroundVideo);
const popIn = spring({
frame,
fps,
config: { damping: 14, stiffness: 180, mass: 0.5 },
});
const slowZoom = interpolate(frame, [0, durationInFrames], [1.0, 1.08], {
extrapolateRight: 'clamp',
});
const fadeOut = interpolate(
frame,
[Math.max(0, durationInFrames - 15), durationInFrames],
[1, 0],
{ extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }
);
return (
<AbsoluteFill style={{ backgroundColor, overflow: 'hidden' }}>
{hasVideo ? (
<OffthreadVideo
src={backgroundVideo as string}
style={{
width: '100%',
height: '100%',
objectFit: 'cover',
transform: `scale(${slowZoom})`,
transformOrigin: 'center center',
}}
muted
/>
) : null}
{hasVideo && overlayOpacity > 0 ? (
<div
style={{
position: 'absolute',
inset: 0,
backgroundColor: `rgba(0,0,0,${overlayOpacity})`,
}}
/>
) : null}
<div
style={{
position: 'absolute',
// Anchor content to verticalAlign% of the viewport height
// (so user-facing control still maps "0 = top, 50 = center,
// 100 = bottom"). Using top + translateY(-50%) avoids the
// paddingTop-% trap where the % resolves to container width.
top: `${verticalAlign}%`,
left: 0,
right: 0,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
transform: `translateY(-50%) scale(${popIn})`,
opacity: fadeOut,
pointerEvents: 'none',
}}
>
<div
style={{
color: titleColor,
fontFamily: titleFamily,
fontSize: titleFontSize,
fontWeight: 800,
textAlign: 'center',
lineHeight: 1.0,
letterSpacing: '-0.03em',
textTransform: 'uppercase',
padding: '0 48px',
textShadow: '0 4px 24px rgba(0,0,0,0.6)',
}}
>
{title}
</div>
{subtitle ? (
<div
style={{
marginTop: 20,
color: subtitleColor,
fontFamily: titleFamily,
fontSize: subtitleFontSize,
fontWeight: 600,
textAlign: 'center',
letterSpacing: '0.12em',
textTransform: 'uppercase',
textShadow: '0 2px 8px rgba(0,0,0,0.8)',
}}
>
{subtitle}
</div>
) : null}
</div>
</AbsoluteFill>
);
}
Schema (Zod)
import { z } from 'zod';
export const Schema = z.object({
backgroundVideo: z
.string()
.describe('$port:video Optional background video (muted)'),
title: z.string().default('Breaking').describe('Title text'),
subtitle: z.string().default('').describe('Subtitle text (optional)'),
titleColor: z
.string()
.default('#FFFFFF')
.describe('$style:color Title color'),
titleFontSize: z
.number()
.default(140)
.describe('$style:size Title font size'),
subtitleColor: z
.string()
.default('#FFD84D')
.describe('$style:color Subtitle color'),
subtitleFontSize: z
.number()
.default(40)
.describe('$style:size Subtitle font size'),
titleFont: z
.enum(['SpaceGrotesk', 'Inter', 'Roboto'])
.default('SpaceGrotesk')
.describe('$style:font Title font'),
verticalAlign: z
.number()
.default(50)
.describe('$style:position Vertical position (% from top)'),
backgroundColor: z
.string()
.default('#05060a')
.describe('$style:color Background color (when no video)'),
overlayOpacity: z
.number()
.default(0.45)
.describe('$style:scale Video darken overlay opacity (0–1)'),
});
Hero Title
hero_titleBig centered title with a subtitle, optional background video. Spring pop-in, slow Ken-Burns zoom, fade-out at the end.
Workflow Inputs (1)
- Optional background video (muted)
backgroundVideoVIDEORequired
Config Fields (10)
titlesubtitletitleFonttitleColorsubtitleColortitleFontSizeverticalAlignoverlayOpacitybackgroundColorsubtitleFontSize
Meta
- Updated
- 7/10/2026