hook-card
remotionui/hook-cardFreeBlock
Short-form opener where the hook rises line by line out of its mask and an underline draws under the promise
Live preview
live · rendered by the registry
Rendered by remotionui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://remotionui.com/r/hook-card.jsonSource
1import { loadFont } from "@remotion/google-fonts/Inter";2import { interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion";3import { CODE_THEMES } from "@/remotion/lib/code-syntax";4import { getSafeAreaPadding, scaleFont } from "@/remotion/lib/layout";5import { EASING } from "@/remotion/lib/motion-tokens";6import { markEmphasis, markerEdges } from "@/remotion/lib/text-emphasis";78const { fontFamily } = loadFont("normal", {9 weights: ["500", "600", "700", "800"],10 subsets: ["latin"],11});1213/** Characters a hook line is broken at when the caller gives no line breaks. */14const LINE_TARGET_LANDSCAPE = 22;15const LINE_TARGET_PORTRAIT = 15;1617/** Width of the space between words, bridged so an underline stays unbroken. */18const WORD_SPACE = "0.26em";1920export type HookCardProps = {21 /**22 * The hook line. Newlines are honoured as written; without them the hook is23 * balanced across lines so no line is left with a single orphan word.24 */25 headline: string;26 /** Small live label that counts in above the hook. */27 kicker?: string;28 /** Supporting line that settles once the hook has landed. */29 subtitle?: string;30 /**31 * Substring of `headline` carrying the promise — it takes the accent colour32 * and an underline that draws under it. Matched case-insensitively.33 */34 emphasis?: string;35 align?: "left" | "center";36 accentColor?: string;37 /** Overrides the page background. */38 backgroundColor?: string;39 theme?: "dark" | "light";40 /** Animation speed multiplier. */41 speed?: number;42};4344/**45 * Breaks a hook into balanced lines at roughly `target` characters, never46 * leaving a line holding a single word. Deterministic, so the reveal masks47 * line up without measuring the DOM.48 */49function balanceLines(text: string, target: number): string[] {50 const words = text.split(/\s+/).filter(Boolean);51 const lines: string[] = [];52 let current = "";5354 for (const word of words) {55 const candidate = current ? `${current} ${word}` : word;56 if (current && candidate.length > target) {57 lines.push(current);58 current = word;59 } else {60 current = candidate;61 }62 }63 if (current) lines.push(current);6465 // Pull a word down rather than stranding the last line on its own.66 if (lines.length > 1) {67 const last = lines[lines.length - 1];68 if (!last.includes(" ")) {69 const previous = lines[lines.length - 2].split(" ");70 if (previous.length > 1) {71 lines[lines.length - 1] = `${previous.pop()} ${last}`;72// … truncated
More from remotionui
All 127 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| ai-composer-showcaseai-composer-showcase | remotionui | Blocks | Free | no deps | |
| ai-generation-canvasai-generation-canvas | remotionui | Blocks | Free | no deps | |
| animated-bar-chartanimated-bar-chart | remotionui | Blocks | Free | no deps | |
| audiogram-sceneaudiogram-scene | remotionui | Blocks | Free | no deps | |
| auto-fit-titleauto-fit-title | remotionui | Blocks | Free | no deps | |
| b-roll-stackb-roll-stack | remotionui | Blocks | Free | no deps | |
| bento-panbento-pan | remotionui | Blocks | Free | no deps | |
| browser-flowbrowser-flow | remotionui | Blocks | Free | no deps |
