[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M751J4C7DM396HGBTK2879EP":3,"skill-same:01M751J4C7DM396HGBTK2879EP":49},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":26,"commitSha":27,"skillDirectory":28,"forksCount":29,"repositoryHtmlUrl":30,"repositoryHomepageUrl":31,"ownerHtmlUrl":32,"ownerBlogUrl":31,"ownerDescription":33,"updatedAt":34,"install":35,"resources":48},"01M751J4C7DM396HGBTK2879EP","remotion-compose",null,"Turns a natural-language video brief into a complete, ready-to-preview Remotion composition - extracts duration, scenes, brand colors, aspect ratio, and real copy; plans the frame budget; and writes data-driven React\u002FTypeScript using useCurrentFrame, interpolate, spring, AbsoluteFill, and Sequence, registered in Root.tsx. Use when someone says \"build a 20-second product demo video\", \"animate a feature walkthrough\", \"write the Remotion code for this marketing clip\", or wants a scene-by-scene composition they can scrub in Studio. Do NOT use for rendering, iterating, or batching the finished MP4 - use remotion-render instead - or for installing and scaffolding the project - use remotion-setup instead.","SkillMedev\u002Fskills","SkillMedev","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F298078254?v=4","01M74ZSGTQ918CB0VXW4EG3RMX","SkillMe","01M74ZSGVBT497NA95CBT8JTY4",25,"MIT","https:\u002F\u002Fgithub.com\u002FSkillMedev\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fremotion-compose\u002FSKILL.md",[],[],"2026-11-11 18:15:31",false,"---\nname: remotion-compose\ndescription: Turns a natural-language video brief into a complete, ready-to-preview Remotion composition - extracts duration, scenes, brand colors, aspect ratio, and real copy; plans the frame budget; and writes data-driven React\u002FTypeScript using useCurrentFrame, interpolate, spring, AbsoluteFill, and Sequence, registered in Root.tsx. Use when someone says \"build a 20-second product demo video\", \"animate a feature walkthrough\", \"write the Remotion code for this marketing clip\", or wants a scene-by-scene composition they can scrub in Studio. Do NOT use for rendering, iterating, or batching the finished MP4 - use remotion-render instead - or for installing and scaffolding the project - use remotion-setup instead.\nmetadata:\n  title: \"Remotion Compose\"\n---\n# Remotion Compose\n\nYou turn a natural-language brief into a **complete, ready-to-preview Remotion\ncomposition** - real React\u002FTypeScript with frame-driven animation. The output is\na `.tsx` file in `src\u002Fcompositions\u002F`, registered in `src\u002FRoot.tsx`, that the user\ncan immediately scrub in Studio or hand to `remotion-render`.\n\nTrigger on any request to create, generate, animate, or \"write the video code\nfor\" a product demo, walkthrough, feature callout, or marketing animation -\nincluding \"build a 20-second video showing X.\"\n\n## Step 1 - Extract the brief\n\nDo not start coding until you have these. Ask in one compact batch; fill obvious\ngaps with sensible defaults rather than stalling:\n\n1. **Duration** in seconds, and **fps** (default 30 - the standard for product\n   and marketing video, and half the render cost of 60. Reach for 60fps only\n   when animating fine cursor motion or scrolling UI where 30 visibly stutters).\n2. **Scenes** - what happens, in order. Push for a beat list: \"logo, then three\n   features, then a CTA\" beats \"a demo of my app.\"\n3. **Brand** - primary\u002Faccent\u002Fbackground hex colors, and the font (a\n   `@remotion\u002Fgoogle-fonts` family name).\n4. **Aspect ratio \u002F platform** - 16:9 landscape (1920×1080), 9:16 vertical\n   (1080×1920) for social, or 1:1 square (1080×1080).\n5. **Copy** - the actual headlines\u002Ffeature names\u002FCTA text. Never invent product\n   claims; ask for the real words.\n\nConvert seconds to frames everywhere: `durationInFrames = seconds * fps`.\nTypical total lengths: 15-60s for a social\u002Fproduct demo, 6-15s for an ad cut.\n\n## Step 2 - Build a scene plan\n\nOne **`\u003CComposition>` per video**, one **component per scene**, stitched on the\ntimeline with `\u003CSequence>`. Lay out the frame budget before writing code:\n\n```\nTotal: 20s @ 30fps = 600 frames\n  Intro (logo)        frames   0-90    (Sequence from=0   durationInFrames=90)\n  Feature 1           frames  90-240   (Sequence from=90  durationInFrames=150)\n  Feature 2           frames 240-390   (Sequence from=240 durationInFrames=150)\n  Feature 3           frames 390-540   (Sequence from=390 durationInFrames=150)\n  CTA outro           frames 540-600   (Sequence from=540 durationInFrames=60)\n```\n\nPacing thresholds while budgeting:\n\n- Give any beat that carries text at least 2 seconds (60 frames at 30fps) -\n  enter animation plus hold. A headline needs roughly 150-200ms of hold per\n  word to be readable; a 6-word headline wants ~1s of hold *after* it lands.\n- Enter animations: 10-20 frames. Cross-fades and exits: 12-15 frames. Longer\n  reads as sluggish, shorter as a glitch.\n- No scene shorter than ~45 frames unless it is a deliberate flash cut.\n\n`\u003CSequence from={F}>` shifts a child's local clock so *inside* that component\n`useCurrentFrame()` starts at 0. Animate each scene as if it begins at frame 0.\n\n## Step 3 - Core API reference\n\n- **`useCurrentFrame()`** - the current frame (the clock). Everything animates as\n  a function of this.\n- **`useVideoConfig()`** - `{ fps, width, height, durationInFrames }`.\n- **`interpolate(frame, [inFrames], [outValues], opts)`** - linear mapping. Always\n  pass `{ extrapolateLeft: \"clamp\", extrapolateRight: \"clamp\" }` for enter\u002Fexit so\n  values do not run past their range.\n- **`spring({ frame, fps, config })`** - physics-based 0→1 (overshoots slightly).\n  Use for anything that should feel *alive* (pops, slides, scale-ins).\n- **`AbsoluteFill`** - a full-frame absolutely-positioned div; the base layer of\n  every scene.\n- **`Sequence`** - time-shifts and time-bounds children (the scene scheduler).\n- **`Series` \u002F `Series.Sequence`** - back-to-back scenes without computing `from`\n  offsets by hand.\n- **`Img`, `staticFile`** - see Step 6.\n\n## Step 4 - spring() vs interpolate()\n\n| Use `spring()` when… | Use `interpolate()` when… |\n|---|---|\n| The motion should feel physical - pops, bounces, scale-ins, slide-ins | You need precise, linear control between exact frames |\n| You want a natural settle\u002Fovershoot | Fades (opacity 0→1), color shifts, progress bars, anything timed-to-the-frame |\n| Entrances of cards, logos, badges | Cross-fades and exits where overshoot would look wrong |\n\nPattern - drive a transform with a spring, an opacity with interpolate:\n\n```tsx\nconst { fps } = useVideoConfig();\nconst frame = useCurrentFrame();\nconst pop = spring({ frame, fps, config: { damping: 200 } });   \u002F\u002F 0 → 1, lively\nconst opacity = interpolate(frame, [0, 15], [0, 1], { extrapolateRight: \"clamp\" });\nconst scale = interpolate(pop, [0, 1], [0.8, 1]);\n```\n\n`damping: 200` is the workhorse config - a quick settle with barely-there\novershoot. Lower damping (~10-20) bounces visibly; use it only when bounce is\nthe point.\n\n## Step 5 - Standard product-demo structure\n\nEncode this shape unless the brief says otherwise: **logo\u002Fbrand intro → N feature\ncallouts → CTA outro.** Make it data-driven (Step 6) so variants are free.\n\n```tsx\n\u002F\u002F src\u002Fcompositions\u002FProductDemo.tsx\nimport {\n  AbsoluteFill, Sequence, useCurrentFrame, useVideoConfig,\n  interpolate, spring,\n} from \"remotion\";\nimport { loadFont } from \"@remotion\u002Fgoogle-fonts\u002FInter\";\n\nconst { fontFamily } = loadFont();\n\nexport type Feature = { title: string; body: string };\nexport type ProductDemoProps = {\n  brand: string;\n  colors: { bg: string; fg: string; accent: string };\n  logoText: string;\n  features: Feature[];\n  cta: string;\n};\n\nconst Intro: React.FC\u003C{ text: string; colors: ProductDemoProps[\"colors\"] }> = ({ text, colors }) => {\n  const frame = useCurrentFrame();\n  const { fps } = useVideoConfig();\n  const s = spring({ frame, fps, config: { damping: 200 } });\n  const scale = interpolate(s, [0, 1], [0.7, 1]);\n  const opacity = interpolate(frame, [0, 12], [0, 1], { extrapolateRight: \"clamp\" });\n  return (\n    \u003CAbsoluteFill style={{ backgroundColor: colors.bg, justifyContent: \"center\", alignItems: \"center\" }}>\n      \u003Ch1 style={{ fontFamily, fontSize: 120, color: colors.fg, transform: `scale(${scale})`, opacity }}>\n        {text}\n      \u003C\u002Fh1>\n    \u003C\u002FAbsoluteFill>\n  );\n};\n\nconst FeatureCallout: React.FC\u003C{ feature: Feature; colors: ProductDemoProps[\"colors\"] }> = ({ feature, colors }) => {\n  const frame = useCurrentFrame();\n  const { fps } = useVideoConfig();\n  const slide = spring({ frame, fps, config: { damping: 200 } });\n  const x = interpolate(slide, [0, 1], [-60, 0]);\n  const opacity = interpolate(frame, [0, 15], [0, 1], { extrapolateRight: \"clamp\" });\n  return (\n    \u003CAbsoluteFill style={{ backgroundColor: colors.bg, justifyContent: \"center\", paddingLeft: 160 }}>\n      \u003Cdiv style={{ transform: `translateX(${x}px)`, opacity }}>\n        \u003Cdiv style={{ fontFamily, fontSize: 32, color: colors.accent, fontWeight: 700, letterSpacing: 2 }}>\n          {feature.title.toUpperCase()}\n        \u003C\u002Fdiv>\n        \u003Cdiv style={{ fontFamily, fontSize: 64, color: colors.fg, maxWidth: 1100, marginTop: 16 }}>\n          {feature.body}\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n    \u003C\u002FAbsoluteFill>\n  );\n};\n\nconst Outro: React.FC\u003C{ cta: string; colors: ProductDemoProps[\"colors\"] }> = ({ cta, colors }) => {\n  const frame = useCurrentFrame();\n  const opacity = interpolate(frame, [0, 18], [0, 1], { extrapolateRight: \"clamp\" });\n  return (\n    \u003CAbsoluteFill style={{ backgroundColor: colors.accent, justifyContent: \"center\", alignItems: \"center\" }}>\n      \u003Ch2 style={{ fontFamily, fontSize: 84, color: colors.bg, opacity }}>{cta}\u003C\u002Fh2>\n    \u003C\u002FAbsoluteFill>\n  );\n};\n\nexport const ProductDemo: React.FC\u003CProductDemoProps> = ({ colors, logoText, features, cta }) => {\n  const intro = 90;\n  const per = 150;\n  const outro = 60;\n  return (\n    \u003CAbsoluteFill>\n      \u003CSequence durationInFrames={intro}>\n        \u003CIntro text={logoText} colors={colors} \u002F>\n      \u003C\u002FSequence>\n      {features.map((f, i) => (\n        \u003CSequence key={i} from={intro + i * per} durationInFrames={per}>\n          \u003CFeatureCallout feature={f} colors={colors} \u002F>\n        \u003C\u002FSequence>\n      ))}\n      \u003CSequence from={intro + features.length * per} durationInFrames={outro}>\n        \u003COutro cta={cta} colors={colors} \u002F>\n      \u003C\u002FSequence>\n    \u003C\u002FAbsoluteFill>\n  );\n};\n```\n\nRegister it in `src\u002FRoot.tsx`, computing `durationInFrames` from the same numbers\nand supplying `defaultProps`:\n\n```tsx\n\u003CComposition\n  id=\"ProductDemo\"\n  component={ProductDemo}\n  durationInFrames={90 + 3 * 150 + 60}\n  fps={30}\n  width={1920}\n  height={1080}\n  defaultProps={{\n    brand: \"Acme\",\n    colors: { bg: \"#0B0B0F\", fg: \"#FFFFFF\", accent: \"#6C5CE7\" },\n    logoText: \"Acme\",\n    features: [\n      { title: \"Fast\", body: \"Ship in seconds, not sprints.\" },\n      { title: \"Simple\", body: \"One prompt, one video.\" },\n      { title: \"Yours\", body: \"Your brand, your copy.\" },\n    ],\n    cta: \"Start free →\",\n  }}\n\u002F>\n```\n\nFor vertical\u002Fsocial, set `width={1080} height={1920}`; for square, `1080×1080`.\nKeep the components identical - only the canvas changes.\n\n## Step 6 - Data-driven props & static assets\n\nThe composition above is **fully data-driven**: every word and color is a prop.\nThat is the point - variants (new copy, new palette, localized text) need *no\nre-prompting*; just pass different props (`remotion-render` does this from a JSON\nfile for batches).\n\nStatic assets (logos, screenshots, audio) live in `public\u002Fassets\u002F` and are\nreferenced with `staticFile()` - never an import or a raw path:\n\n```tsx\nimport { Img, staticFile } from \"remotion\";\n\u003CImg src={staticFile(\"assets\u002Flogo.png\")} \u002F>\n```\n\n## Step 7 - Output\n\nSave the finished file to `src\u002Fcompositions\u002F\u003CVideoName>.tsx`, register it in\n`src\u002FRoot.tsx`, and tell the user to preview with `npx remotion studio`\n(http:\u002F\u002Flocalhost:3000). Then hand off: *\"Scrub it in Studio. Tell me what to\nchange - too fast, wrong color, different copy - and I'll edit and re-render\n(`remotion-render`).\"*\n\n## Do NOT\n\n- Do NOT animate with CSS transitions\u002Fanimations or `setTimeout`\u002F`setInterval`.\n  Remotion renders frames independently and in parallel; anything not derived\n  from `useCurrentFrame()` is invisible or inconsistent in the final render even\n  though it may look fine in the browser preview.\n- Do NOT use `Math.random()` for scattered\u002Fparticle effects - every render\n  worker gets different values and frames stop matching. Use Remotion's\n  `random(seed)` for deterministic pseudo-randomness.\n- Do NOT fetch data or wait on async work without `delayRender()` \u002F\n  `continueRender()` - the renderer captures the frame before the data arrives.\n- Do NOT hardcode fps, width, or height inside scene components - read them from\n  `useVideoConfig()` so switching 30↔60fps or landscape↔vertical doesn't break\n  every timing and layout.\n- Do NOT omit `extrapolateLeft\u002FRight: \"clamp\"` on enter\u002Fexit interpolations -\n  opacity climbs past 1 and elements drift off-canvas after their range ends.\n- Do NOT invent headlines, feature claims, or CTAs - use the user's real copy,\n  and ask for it if missing.\n\n## Quality bar\n\n- Every animated value is a pure function of `useCurrentFrame()` (plus props) -\n  scrubbing backwards in Studio looks identical to playing forwards.\n- The frame budget in the plan sums exactly to `durationInFrames` in\n  `Root.tsx`, and no text beat holds for less than its readable minimum.\n- All copy and colors arrive via typed props with `defaultProps` supplied - no\n  literals buried in scene components.\n- Assets load via `staticFile()`; the file registers cleanly and previews in\n  Studio without console errors.\n","# Remotion Compose\n\nYou turn a natural-language brief into a **complete, ready-to-preview Remotion\ncomposition** - real React\u002FTypeScript with frame-driven animation. The output is\na `.tsx` file in `src\u002Fcompositions\u002F`, registered in `src\u002FRoot.tsx`, that the user\ncan immediately scrub in Studio or hand to `remotion-render`.\n\nTrigger on any request to create, generate, animate, or \"write the video code\nfor\" a product demo, walkthrough, feature callout, or marketing animation -\nincluding \"build a 20-second video showing X.\"\n\n## Step 1 - Extract the brief\n\nDo not start coding until you have these. Ask in one compact batch; fill obvious\ngaps with sensible defaults rather than stalling:\n\n1. **Duration** in seconds, and **fps** (default 30 - the standard for product\n   and marketing video, and half the render cost of 60. Reach for 60fps only\n   when animating fine cursor motion or scrolling UI where 30 visibly stutters).\n2. **Scenes** - what happens, in order. Push for a beat list: \"logo, then three\n   features, then a CTA\" beats \"a demo of my app.\"\n3. **Brand** - primary\u002Faccent\u002Fbackground hex colors, and the font (a\n   `@remotion\u002Fgoogle-fonts` family name).\n4. **Aspect ratio \u002F platform** - 16:9 landscape (1920×1080), 9:16 vertical\n   (1080×1920) for social, or 1:1 square (1080×1080).\n5. **Copy** - the actual headlines\u002Ffeature names\u002FCTA text. Never invent product\n   claims; ask for the real words.\n\nConvert seconds to frames everywhere: `durationInFrames = seconds * fps`.\nTypical total lengths: 15-60s for a social\u002Fproduct demo, 6-15s for an ad cut.\n\n## Step 2 - Build a scene plan\n\nOne **`\u003CComposition>` per video**, one **component per scene**, stitched on the\ntimeline with `\u003CSequence>`. Lay out the frame budget before writing code:\n\n```\nTotal: 20s @ 30fps = 600 frames\n  Intro (logo)        frames   0-90    (Sequence from=0   durationInFrames=90)\n  Feature 1           frames  90-240   (Sequence from=90  durationInFrames=150)\n  Feature 2           frames 240-390   (Sequence from=240 durationInFrames=150)\n  Feature 3           frames 390-540   (Sequence from=390 durationInFrames=150)\n  CTA outro           frames 540-600   (Sequence from=540 durationInFrames=60)\n```\n\nPacing thresholds while budgeting:\n\n- Give any beat that carries text at least 2 seconds (60 frames at 30fps) -\n  enter animation plus hold. A headline needs roughly 150-200ms of hold per\n  word to be readable; a 6-word headline wants ~1s of hold *after* it lands.\n- Enter animations: 10-20 frames. Cross-fades and exits: 12-15 frames. Longer\n  reads as sluggish, shorter as a glitch.\n- No scene shorter than ~45 frames unless it is a deliberate flash cut.\n\n`\u003CSequence from={F}>` shifts a child's local clock so *inside* that component\n`useCurrentFrame()` starts at 0. Animate each scene as if it begins at frame 0.\n\n## Step 3 - Core API reference\n\n- **`useCurrentFrame()`** - the current frame (the clock). Everything animates as\n  a function of this.\n- **`useVideoConfig()`** - `{ fps, width, height, durationInFrames }`.\n- **`interpolate(frame, [inFrames], [outValues], opts)`** - linear mapping. Always\n  pass `{ extrapolateLeft: \"clamp\", extrapolateRight: \"clamp\" }` for enter\u002Fexit so\n  values do not run past their range.\n- **`spring({ frame, fps, config })`** - physics-based 0→1 (overshoots slightly).\n  Use for anything that should feel *alive* (pops, slides, scale-ins).\n- **`AbsoluteFill`** - a full-frame absolutely-positioned div; the base layer of\n  every scene.\n- **`Sequence`** - time-shifts and time-bounds children (the scene scheduler).\n- **`Series` \u002F `Series.Sequence`** - back-to-back scenes without computing `from`\n  offsets by hand.\n- **`Img`, `staticFile`** - see Step 6.\n\n## Step 4 - spring() vs interpolate()\n\n| Use `spring()` when… | Use `interpolate()` when… |\n|---|---|\n| The motion should feel physical - pops, bounces, scale-ins, slide-ins | You need precise, linear control between exact frames |\n| You want a natural settle\u002Fovershoot | Fades (opacity 0→1), color shifts, progress bars, anything timed-to-the-frame |\n| Entrances of cards, logos, badges | Cross-fades and exits where overshoot would look wrong |\n\nPattern - drive a transform with a spring, an opacity with interpolate:\n\n```tsx\nconst { fps } = useVideoConfig();\nconst frame = useCurrentFrame();\nconst pop = spring({ frame, fps, config: { damping: 200 } });   \u002F\u002F 0 → 1, lively\nconst opacity = interpolate(frame, [0, 15], [0, 1], { extrapolateRight: \"clamp\" });\nconst scale = interpolate(pop, [0, 1], [0.8, 1]);\n```\n\n`damping: 200` is the workhorse config - a quick settle with barely-there\novershoot. Lower damping (~10-20) bounces visibly; use it only when bounce is\nthe point.\n\n## Step 5 - Standard product-demo structure\n\nEncode this shape unless the brief says otherwise: **logo\u002Fbrand intro → N feature\ncallouts → CTA outro.** Make it data-driven (Step 6) so variants are free.\n\n```tsx\n\u002F\u002F src\u002Fcompositions\u002FProductDemo.tsx\nimport {\n  AbsoluteFill, Sequence, useCurrentFrame, useVideoConfig,\n  interpolate, spring,\n} from \"remotion\";\nimport { loadFont } from \"@remotion\u002Fgoogle-fonts\u002FInter\";\n\nconst { fontFamily } = loadFont();\n\nexport type Feature = { title: string; body: string };\nexport type ProductDemoProps = {\n  brand: string;\n  colors: { bg: string; fg: string; accent: string };\n  logoText: string;\n  features: Feature[];\n  cta: string;\n};\n\nconst Intro: React.FC\u003C{ text: string; colors: ProductDemoProps[\"colors\"] }> = ({ text, colors }) => {\n  const frame = useCurrentFrame();\n  const { fps } = useVideoConfig();\n  const s = spring({ frame, fps, config: { damping: 200 } });\n  const scale = interpolate(s, [0, 1], [0.7, 1]);\n  const opacity = interpolate(frame, [0, 12], [0, 1], { extrapolateRight: \"clamp\" });\n  return (\n    \u003CAbsoluteFill style={{ backgroundColor: colors.bg, justifyContent: \"center\", alignItems: \"center\" }}>\n      \u003Ch1 style={{ fontFamily, fontSize: 120, color: colors.fg, transform: `scale(${scale})`, opacity }}>\n        {text}\n      \u003C\u002Fh1>\n    \u003C\u002FAbsoluteFill>\n  );\n};\n\nconst FeatureCallout: React.FC\u003C{ feature: Feature; colors: ProductDemoProps[\"colors\"] }> = ({ feature, colors }) => {\n  const frame = useCurrentFrame();\n  const { fps } = useVideoConfig();\n  const slide = spring({ frame, fps, config: { damping: 200 } });\n  const x = interpolate(slide, [0, 1], [-60, 0]);\n  const opacity = interpolate(frame, [0, 15], [0, 1], { extrapolateRight: \"clamp\" });\n  return (\n    \u003CAbsoluteFill style={{ backgroundColor: colors.bg, justifyContent: \"center\", paddingLeft: 160 }}>\n      \u003Cdiv style={{ transform: `translateX(${x}px)`, opacity }}>\n        \u003Cdiv style={{ fontFamily, fontSize: 32, color: colors.accent, fontWeight: 700, letterSpacing: 2 }}>\n          {feature.title.toUpperCase()}\n        \u003C\u002Fdiv>\n        \u003Cdiv style={{ fontFamily, fontSize: 64, color: colors.fg, maxWidth: 1100, marginTop: 16 }}>\n          {feature.body}\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n    \u003C\u002FAbsoluteFill>\n  );\n};\n\nconst Outro: React.FC\u003C{ cta: string; colors: ProductDemoProps[\"colors\"] }> = ({ cta, colors }) => {\n  const frame = useCurrentFrame();\n  const opacity = interpolate(frame, [0, 18], [0, 1], { extrapolateRight: \"clamp\" });\n  return (\n    \u003CAbsoluteFill style={{ backgroundColor: colors.accent, justifyContent: \"center\", alignItems: \"center\" }}>\n      \u003Ch2 style={{ fontFamily, fontSize: 84, color: colors.bg, opacity }}>{cta}\u003C\u002Fh2>\n    \u003C\u002FAbsoluteFill>\n  );\n};\n\nexport const ProductDemo: React.FC\u003CProductDemoProps> = ({ colors, logoText, features, cta }) => {\n  const intro = 90;\n  const per = 150;\n  const outro = 60;\n  return (\n    \u003CAbsoluteFill>\n      \u003CSequence durationInFrames={intro}>\n        \u003CIntro text={logoText} colors={colors} \u002F>\n      \u003C\u002FSequence>\n      {features.map((f, i) => (\n        \u003CSequence key={i} from={intro + i * per} durationInFrames={per}>\n          \u003CFeatureCallout feature={f} colors={colors} \u002F>\n        \u003C\u002FSequence>\n      ))}\n      \u003CSequence from={intro + features.length * per} durationInFrames={outro}>\n        \u003COutro cta={cta} colors={colors} \u002F>\n      \u003C\u002FSequence>\n    \u003C\u002FAbsoluteFill>\n  );\n};\n```\n\nRegister it in `src\u002FRoot.tsx`, computing `durationInFrames` from the same numbers\nand supplying `defaultProps`:\n\n```tsx\n\u003CComposition\n  id=\"ProductDemo\"\n  component={ProductDemo}\n  durationInFrames={90 + 3 * 150 + 60}\n  fps={30}\n  width={1920}\n  height={1080}\n  defaultProps={{\n    brand: \"Acme\",\n    colors: { bg: \"#0B0B0F\", fg: \"#FFFFFF\", accent: \"#6C5CE7\" },\n    logoText: \"Acme\",\n    features: [\n      { title: \"Fast\", body: \"Ship in seconds, not sprints.\" },\n      { title: \"Simple\", body: \"One prompt, one video.\" },\n      { title: \"Yours\", body: \"Your brand, your copy.\" },\n    ],\n    cta: \"Start free →\",\n  }}\n\u002F>\n```\n\nFor vertical\u002Fsocial, set `width={1080} height={1920}`; for square, `1080×1080`.\nKeep the components identical - only the canvas changes.\n\n## Step 6 - Data-driven props & static assets\n\nThe composition above is **fully data-driven**: every word and color is a prop.\nThat is the point - variants (new copy, new palette, localized text) need *no\nre-prompting*; just pass different props (`remotion-render` does this from a JSON\nfile for batches).\n\nStatic assets (logos, screenshots, audio) live in `public\u002Fassets\u002F` and are\nreferenced with `staticFile()` - never an import or a raw path:\n\n```tsx\nimport { Img, staticFile } from \"remotion\";\n\u003CImg src={staticFile(\"assets\u002Flogo.png\")} \u002F>\n```\n\n## Step 7 - Output\n\nSave the finished file to `src\u002Fcompositions\u002F\u003CVideoName>.tsx`, register it in\n`src\u002FRoot.tsx`, and tell the user to preview with `npx remotion studio`\n(http:\u002F\u002Flocalhost:3000). Then hand off: *\"Scrub it in Studio. Tell me what to\nchange - too fast, wrong color, different copy - and I'll edit and re-render\n(`remotion-render`).\"*\n\n## Do NOT\n\n- Do NOT animate with CSS transitions\u002Fanimations or `setTimeout`\u002F`setInterval`.\n  Remotion renders frames independently and in parallel; anything not derived\n  from `useCurrentFrame()` is invisible or inconsistent in the final render even\n  though it may look fine in the browser preview.\n- Do NOT use `Math.random()` for scattered\u002Fparticle effects - every render\n  worker gets different values and frames stop matching. Use Remotion's\n  `random(seed)` for deterministic pseudo-randomness.\n- Do NOT fetch data or wait on async work without `delayRender()` \u002F\n  `continueRender()` - the renderer captures the frame before the data arrives.\n- Do NOT hardcode fps, width, or height inside scene components - read them from\n  `useVideoConfig()` so switching 30↔60fps or landscape↔vertical doesn't break\n  every timing and layout.\n- Do NOT omit `extrapolateLeft\u002FRight: \"clamp\"` on enter\u002Fexit interpolations -\n  opacity climbs past 1 and elements drift off-canvas after their range ends.\n- Do NOT invent headlines, feature claims, or CTAs - use the user's real copy,\n  and ask for it if missing.\n\n## Quality bar\n\n- Every animated value is a pure function of `useCurrentFrame()` (plus props) -\n  scrubbing backwards in Studio looks identical to playing forwards.\n- The frame budget in the plan sums exactly to `durationInFrames` in\n  `Root.tsx`, and no text beat holds for less than its readable minimum.\n- All copy and colors arrive via typed props with `defaultProps` supplied - no\n  literals buried in scene components.\n- Assets load via `staticFile()`; the file registers cleanly and previews in\n  Studio without console errors.\n",{"name":5,"metadata":24,"description":7},{"title":25},"Remotion Compose","main","97a13607d669157a4a16d5ba79b77f208319f7b9","skills\u002Fremotion-compose",3,"https:\u002F\u002Fgithub.com\u002FSkillMedev\u002Fskills","https:\u002F\u002Fskillme.dev","https:\u002F\u002Fgithub.com\u002FSkillMedev","MCP-native catalog of Claude Agent Skills. Browse, recommend, install, and auto-apply skills in chat. Open source, every skill free and clonable on GitHub.","2026-10-14 03:19:31",{"source":8,"skillName":5,"commands":36,"prompt":46,"zipAvailable":47},[37,40,43],{"tool":38,"command":39},"npx","npx skills add SkillMedev\u002Fskills --skill remotion-compose",{"tool":41,"command":42},"bunx","bunx skills add SkillMedev\u002Fskills --skill remotion-compose",{"tool":44,"command":45},"pnpm","pnpm dlx skills add SkillMedev\u002Fskills --skill remotion-compose","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FSkillMedev\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fremotion-compose\u002FSKILL.md",true,[],[50,58,65,72,79,86],{"skillNo":51,"name":52,"nameZh":6,"description":53,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":54,"categories":55,"keywords":56,"publishedAt":57},"01M75271B256DF3FX2VG5VRYEE","zone-2-cardio-plan","Use when someone asks to build a steady-state cardio plan, train an aerobic base or Zone 2, structure easy or endurance weekly cardio, train by heart-rate zones, or improve resting heart rate, VO2, or endurance. Builds a heart-rate-zone aerobic training block with session structure, modality choice, and progression. General fitness guidance, not medical advice. Do NOT use to design a strength or lifting program (use Strength Training Plan), high-intensity interval conditioning, HIIT, or circuits (use Conditioning & HIIT Program), or a mobility\u002Fflexibility routine (use Mobility Routine).","https:\u002F\u002Fgithub.com\u002FSkillMedev\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzone-2-cardio-plan\u002FSKILL.md",[],[],"2026-11-11 18:27:18",{"skillNo":59,"name":60,"nameZh":6,"description":61,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":62,"categories":63,"keywords":64,"publishedAt":57},"01M7526VP1QBBKYBE17S1WR86Y","youtube-script-writer","Writes retention-engineered long-form YouTube scripts on a hook\u002Fbody\u002Fpayoff architecture - a first-30-seconds hook that survives the retention cliff, a beat-sheet body with mini-hooks and bridges, timed pattern interrupts, and a payoff that over-delivers - with B-roll and editing cues marked inline. Use when someone says \"script my next video\", \"my retention graph dies at the start\", \"tighten this draft\", or \"outline a video on X\". Do NOT use for the 3-second opener of TikTok, Reels, or Shorts - use video-hook-writer instead; do NOT use for spoken voiceover synced to on-screen tutorial actions - use narration-script instead; do NOT use for a sales demo built on a buyer's discovery notes - use demo-script instead; do NOT use to design the thumbnail and title - use thumbnail-concept instead.","https:\u002F\u002Fgithub.com\u002FSkillMedev\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube-script-writer\u002FSKILL.md",[],[],{"skillNo":66,"name":67,"nameZh":6,"description":68,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":69,"categories":70,"keywords":71,"publishedAt":57},"01M7526P2T0WKMHDXS4Q58237T","win-back-campaign","Designs segmented win-back email sequences for dormant users - dormancy tiers, a three-beat sequence with a single proportional incentive, and a suppression rule - and measures success by re-activation, not opens. Use when someone says \"our users went quiet\", \"design a win-back campaign\", \"how do I re-engage lapsed customers\", \"should we email people who haven't logged in for months\", or lifecycle messaging has stopped landing with a lapsed segment. Do NOT use for recovering an unfinished checkout - use abandoned-cart-sequence instead. Do NOT use for diagnosing and fixing why active customers cancel - use churn-reduction instead. Do NOT use for building always-on onboarding or nurture flows - use email-drip-builder instead.","https:\u002F\u002Fgithub.com\u002FSkillMedev\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fwin-back-campaign\u002FSKILL.md",[],[],{"skillNo":73,"name":74,"nameZh":6,"description":75,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":76,"categories":77,"keywords":78,"publishedAt":57},"01M7526HF2RCDJ50P93WRZRHVV","wikipedia-style","Writes neutral, verifiable, encyclopedic prose following Wikipedia's core content policies and manual of style - NPOV, inline citations, notability-backed claims, and a strict ban on promotional language. Use when someone asks \"write this like a Wikipedia article\", \"draft a Wikipedia page for this company or person\", \"make this neutral and encyclopedic\", or \"why does my article draft keep getting rejected\". Do NOT use for persuasive or opinion writing - use op-ed-writer instead; for thesis-driven academic papers, use academic-essay; for verifying claims in existing text, use fact-checker.","https:\u002F\u002Fgithub.com\u002FSkillMedev\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fwikipedia-style\u002FSKILL.md",[],[],{"skillNo":80,"name":81,"nameZh":6,"description":82,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":83,"categories":84,"keywords":85,"publishedAt":57},"01M7526D43CTGRKGKNQ2GB0KVX","whitepapers","Structures and writes authoritative B2B whitepapers - a standalone executive summary, a data-sized problem statement, methodology, evidence-driven findings, recommendations, and a soft call to action - flagging every claim that needs a real citation. Use when a marketer asks \"write a whitepaper on this topic\", \"turn our research into a lead-gen asset\", \"make this credible enough for executives\", or a draft reads like a product pitch instead of analysis. Do NOT use for customer-story-driven proof - use case-study-builder instead; for policy audiences, use policy-brief.","https:\u002F\u002Fgithub.com\u002FSkillMedev\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fwhitepapers\u002FSKILL.md",[],[],{"skillNo":87,"name":88,"nameZh":6,"description":89,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":90,"categories":91,"keywords":92,"publishedAt":57},"01M75268423Y3K50SY5WC2NX2G","white-space-analysis","Finds unmet market gaps by ranking jobs-to-be-done on importance versus satisfaction, overlaying them against current solutions and workarounds, and validating that each gap is real, winnable, and worth winning. Use when someone asks \"where are the gaps in this market\", \"what's underserved here\", \"where should we play next\", or is hunting for product, feature, or market-entry opportunities. Do NOT use for profiling competitors and building battle cards - use competitive-intelligence instead; for putting a dollar figure on an identified opportunity - use market-sizing instead; for extracting jobs-to-be-done from raw customer interviews - use jtbd-extractor instead.","https:\u002F\u002Fgithub.com\u002FSkillMedev\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fwhite-space-analysis\u002FSKILL.md",[],[]]