import React from 'react';
import {
AbsoluteFill,
interpolate,
Sequence,
spring,
useCurrentFrame,
useVideoConfig,
} from 'remotion';
import { palette } from './contract';
const fontFamily = 'Archivo, sans-serif';
const bg = palette.background;
const fg = palette.foreground;
const accent = palette.accent;
const secondary = palette.secondary;
const muted = palette.muted;
const CLAMP = {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
} as const;
const rgba = (hex: string, alpha: number): string => {
const h = hex.replace('#', '');
const r = parseInt(h.slice(0, 2), 16);
const g = parseInt(h.slice(2, 4), 16);
const b = parseInt(h.slice(4, 6), 16);
return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')';
};
const delayedSpring = (
frame: number,
fps: number,
delay: number,
config: { damping?: number; stiffness?: number; mass?: number } = {}
): number => {
return spring({
frame: Math.max(0, frame - delay),
fps,
config: { damping: 200, ...config },
});
};
const Backdrop: React.FC = () => (
);
const Intro: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const enter = delayedSpring(frame, fps, 0, { damping: 26, stiffness: 110 });
const scale = interpolate(enter, [0, 1], [0.92, 1]);
const titleOpacity = interpolate(frame, [0, 12], [0, 1], CLAMP);
const subOpacity = interpolate(frame, [26, 44], [0, 1], CLAMP);
const subRise = interpolate(
delayedSpring(frame, fps, 26, { damping: 24 }),
[0, 1],
[24, 0]
);
const exit = interpolate(frame, [76, 89], [1, 0], CLAMP);
const bars = [accent, secondary, fg];
return (
{bars.map((c, i) => {
const p = delayedSpring(frame, fps, 4 + i * 6, {
damping: 18,
stiffness: 130,
});
return (
);
})}
Make room for focused work
);
};
const FeatureScene: React.FC<{
index: string;
kicker: string;
title: string;
panelTitle: string;
children: React.ReactNode;
}> = ({ index, kicker, title, panelTitle, children }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const enter = delayedSpring(frame, fps, 0, { damping: 26 });
const exit = interpolate(frame, [50, 59], [1, 0], CLAMP);
const textRise = interpolate(enter, [0, 1], [36, 0]);
const panelSlide = interpolate(enter, [0, 1], [56, 0]);
return (
{[muted, secondary, accent].map((c, i) => (
))}
{panelTitle}
{children}
);
};
const Board: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const cols = [
{ name: 'Backlog', x: 0 },
{ name: 'This week', x: 300 },
{ name: 'Done', x: 600 },
];
const cards = [
{
label: 'Design review',
tag: 'Product',
initials: 'AK',
from: 0,
to: 1,
delay: 10,
avatar: secondary,
row: 0,
},
{
label: 'Sprint planning',
tag: 'Ops',
initials: 'JM',
from: 0,
to: 1,
delay: 20,
avatar: accent,
row: 1,
},
{
label: 'Launch checklist',
tag: 'Growth',
initials: 'RS',
from: 1,
to: 2,
delay: 34,
avatar: secondary,
row: 2,
},
];
const highlight = delayedSpring(frame, fps, 10, { damping: 22 });
return (
{cols.map((c) => (
{c.name}
))}
{cards.map((card, i) => {
const move = delayedSpring(frame, fps, card.delay, {
damping: 28,
stiffness: 120,
});
const x = interpolate(
move,
[0, 1],
[cols[card.from].x, cols[card.to].x]
);
const enterP = delayedSpring(frame, fps, 4 + i * 4, { damping: 26 });
return (
{card.label}
{card.tag}
{card.initials}
);
})}
);
};
const Schedule: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const enter = delayedSpring(frame, fps, 2, { damping: 26 });
const focusP = delayedSpring(frame, fps, 10, {
damping: 24,
stiffness: 90,
});
const focusH = interpolate(focusP, [0, 1], [30, 336]);
const meetings = [
{ label: 'Standup', time: '9:00 - 9:30', top: 20, height: 64, dismissAt: 999 },
{
label: '1:1 with Sam',
time: '10:00 - 11:00',
top: 120,
height: 120,
dismissAt: 16,
},
{
label: 'Team sync',
time: '1:00 - 2:00',
top: 336,
height: 120,
dismissAt: 24,
},
];
const hours = [
{ label: '9 AM', top: 20 },
{ label: '10', top: 104 },
{ label: '11', top: 188 },
{ label: '12', top: 272 },
{ label: '1 PM', top: 356 },
];
return (
{hours.map((h) => (
{h.label}
))}
{meetings.map((m) => {
const d =
m.dismissAt > 100
? 0
: delayedSpring(frame, fps, m.dismissAt, { damping: 26 });
return (
);
})}
10:00 AM - 1:00 PM - Focus time
);
};
const Progress: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const values = [0.42, 0.58, 0.5, 0.72, 0.64, 0.86, 1];
const days = ['M', 'T', 'W', 'T', 'F', 'S', 'S'];
const chartH = 330;
const numberP = delayedSpring(frame, fps, 14, { damping: 30 });
const value = interpolate(numberP, [0, 1], [0, 18.5]);
return (
Focus hours - this week
{value.toFixed(1)}h
{values.map((v, i) => {
const p = delayedSpring(frame, fps, 6 + i * 5, {
damping: 20,
stiffness: 120,
});
const h = v * chartH * p;
const last = i === values.length - 1;
return (
);
})}
{days.map((d, i) => (
{d}
))}
);
};
const Outro: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const enter = delayedSpring(frame, fps, 0, { damping: 26 });
const subOpacity = interpolate(frame, [22, 34], [0, 1], CLAMP);
const underline = delayedSpring(frame, fps, 10, { damping: 24 });
return (
Relay
Start your next week
with Relay
Plan together
·
Protect focus time
·
See progress
);
};
export const BenchmarkVideo: React.FC = () => {
return (
);
};