Communitygithub.com

remotion-design-system

A hard-constrained design system for talking-head HUD packaging: token semantics, component specs, and ironclad rules. Must read before making any visual decisions or creating wrapper elements. Trigger words: design system, components, tokens, fonts, color scheme.

What is remotion-design-system?

This skill defines the Remotion talking-head packaging design system (v1 hub). It is a set of hard constraints—ironclad rules, token semantics, component specifications, and a collection of past mistakes—that an AI agent must follow when creating or modifying HUD wrapper elements for talking-head videos. The system is built around a single source of truth (`src/design/tokens.ts`) for all numerical and semantic values, preventing code-drift between the skill document and actual source code. It includes a reference routing table (e.g., `references/specs.md` for measured values), a component library (SideLabel, HeroText, Chip, Breathe, etc.), motion hooks, and a QC probe (`QcProbe`) for overlap detection. The skill also documents a series of engineering substrate files required for cold-starting a new episode, a bootstrap sequence when `src/` is entirely empty, and an extensive gotcha index (e.g., #01: Chinese text silently falling back to Songti due to tree-shaking, #06: `backgroundClip: text` rendering dark). It is designed for Remotion projects on Mac/Linux and targets agents that generate or modify TSX components and render Remotion compositions.

Works with~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/jincheng2026/jc-remotion-skills/tree/main/skills/remotion-design-system

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

Remotion Talking-Head Packaging Design System (v1 hub)

Style target: Replicate the talking-head HUD packaging of the benchmark video (reference images in project refs/). This file is the hard-constraint hub: ironclad rules + tokens semantics + component quick-reference + mistake archive; detailed rules are in references/ (the settlement landing point for the 77-gap deep comparison of 2026-07-16). Any visual decision not documented in this system must first be added to the corresponding section file before writing any code.

Section Routing Table (which file to read for what)

SectionFileScope
Spec Sheet (check before writing)references/specs.mdTrue source of measured values from benchmark: card width/font size/position/motion frame counts – verify before assembly; if not in table, measure and backfill; no eyeballing
Layout & Compositionreferences/layout.mdThree zones, takeover mechanism (A-level without PiP, darken/slow-motion rejected), one center-of-gravity per screen, grouping templates, bite positioning
Typographyreferences/typography.mdSingle largest font, h1 duty, hero sandwich, SideLabel title variant
Color & Texturereferences/texture.mdJelly block/glow, SURFACE light/dark variants, stamp specification single line, ≤2 color systems per screen, no naked text on bright surfaces
Motionreferences/motion.mduseEnter/useExit, beat-by-beat entry, build-up, service interval, Breathe, clean screen
Information Designreferences/infographic.mdChart selections, metaphorical graphics, waffle, big number three layers, micrographic vocabulary
Narrative/Rhythm/Subtitles/Broll/Audioremotion-assembly's references/narrative / rhythm / subtitle / broll / audio
Mistake Index (symptom → repair reference)independent skill remotion-gotchas-indexLook up the repair location of any "Mistake #NN" mentioned in the full text; check this first for rendering anomalies or visual issues

Engineering Substrate Checklist (substrate — confirm in place before cold-starting / starting a new episode)

This design system is a shared substrate for the entire repository; it is not rebuilt per episode. The following files are prerequisites for the entire pipeline (assembly "input validation → frame output → three gates" + motion §12 gating); a new episode reuses them by default, only adding its own composition + subtitles + Scene, without rebuilding the substrate. Cold-start readers (forbidden from reading src/) often stumble here — first confirm the files exist using this table; to get specific values, open the corresponding file and read it; do not expect the skill to copy them for you.

Substrate FileWhat It ContainsHow to Use for Cold Start
src/design/tokens.tsSingle source of truth for values: COLOR (4 semantic + base hex), SURFACE, FONT, GRADIENT, SIZE ladder (kicker…mega), GRID, RADIUS, SAFE, MOTION frame countsFor SIZE.mega, semantic color hex etc., read this file directly — the skill deliberately does not copy them (to prevent snapshot/code drift, see §1); if file missing = substrate missing, build substrate first
src/design/*.tsxComponent library (§3 full table: SideLabel/HeroText/Chip…) + FontGuard.tsx (font gatekeeper, Mistake #01) + qc.tsx (QC probe QcProbe: per-frame check of [data-qc] boxes for overlap, Mistake #29)Before writing packaging elements, check §3 component table to locate the file
src/design/motion.tsuseEnter/useExit/usePop low-level hooks + MOTION constantsThe hooks referenced by motion.md rules are all here
src/design/fonts.ts@fontsource local font registration (Mistake #01: webfont via network silently falls back to Songti)package.json sideEffects must include it
src/koubo/scenes/shared.tsxAssembly layer gatekeeper quadruple Enter/DimAfter/Exit/ClearOut (motion §12) + Place positioning box + qcAllow QC bite whitelist (assembly §2.5 gate ①)When writing Scene assembly, directly wrap these wrappers; do not hand-roll opacity
src/Root.tsxComposition registration (existing <Composition id="Koubo-0715-01" fps={30} …/>)Add one <Composition> here for the new episode, id is arbitrary; the segment-render script must target this id (see assembly §6 render-service for changing id)
src/koubo/Per-episode files: subtitles-<episode-name>.ts / SubtitleTrack.tsx / <episode-main-component>.tsx / scenes/SceneNN.tsxFor a new episode, only add files in this layer (structure in assembly §4)

True from-zero bootstrap (src/ completely empty): Until the substrate is in place, the assembly pipeline cannot start (no tokens available, no first frame, no three gates). Order = build substrate first, then talk about assembly: ① tokens.ts set value source → ② fonts.ts + FontGuard.tsx (Mistake #01) → ③ motion.ts three hooks → ④ §3 component library → ⑤ src/koubo/scenes/shared.tsx gatekeeper quadruple + Place/qcAllow → ⑥ src/design/qc.tsx QC probe → ⑦ src/Root.tsx register composition. Use the src/design in this repo as a reference implementation; after porting, values still read from tokens.ts, do not backfill into the skill.

0. Six Ironclad Rules

  1. Information area defaults to left, person on right: The information area defaults to the left (author defaults determined by user), HUD anchored to the left side and grows vertically, person area is forbidden; but ask the user about composition for each episode; do not assume. Three-zone coordinates and takeover exceptions are in references/layout.md rule 1.
  2. All text is two-layered: English UPPERCASE wide-letter-spacing kicker (small) + Chinese bold black (large). A component with only one layer of text is considered incomplete.
  3. Highlight only one word per sentence: In Chinese titles, emphasize/color-change at most one word; the color must come from the four-color semantic system.
  4. Real footage is primary, Remotion is a compositor, not a drawing board (2026-07-16 seg01 ruling upgrade): All "product experience" type visuals (typing/generation/interface/demonstration) must use actual footage editing — self-recording screen > official video > web screenshot > UI mockup card; hand-drawn is only for the HUD layer (side labels/subtitles/charts/stamps/metaphorical graphics). Using CSS to hand-craft what professional motion teams do with compositors will always fail; the root cause of "it almost looks right but not quite/far off" is using a drawing board to fight a compositor's battle. Capture and editing pipeline is in assembly references/broll.md.
  5. No naked text on bright surfaces (2026-07-16 deep comparison ruling, the top cross-section rule spanning 6 sections): On bright backgrounds (whiteboard/white wall/light takeover background), no text without a background — all text must sit on a dark background plate (SURFACE.darkPlate), card body, or light surface variant; textShadow is not a solution. Detailed rules are in references/texture.md.
  6. One dominant layer per screen: Each information screen has exactly one "largest font/largest element"; outside the main area, at most 2 chip-level small elements. Detailed rules are in references/typography.md and layout.md.

1. Design Tokens

Single source of truth = src/design/tokens.ts; all components are only allowed to import values from there. This section only documents semantics and usage rules, not values — there was a snapshot/code drift incident before (2026-07-16, multiple mismatches found including kicker/h1, etc.), code is the authority.

  • Four-color semantics (the only allowed accent colors): blue = definition/method/neutral progression; green = positive/low threshold/activated; yellow = opportunity/warning/controversy/turn; red = negative/trap/crisis. Base colors include white/grey/greyDim/cardBg/cardStroke.
  • Fonts (two layers, do not mix): MG packaging layer — FONT.zh = Noto Sans SC (only Chinese font for packaging layer; titles should always use FONT.zhHeavy); FONT.en = Inter (kicker/body text/numbers); FONT.enTitle = Archivo Black (English titles and large conclusions; narrow super-black, weight always 400 — Inter cannot carry large text, 2026-07 glyph comparison conclusion). Bottom subtitle layer — FONT.subZh = PingFang SC 400 / FONT.subEn = Inter 300 (after 2026-07-26 five-option real-shot comparison, user ruling): subtitles are a reading-following layer, not a packaging layer; only a thin weight has the quality of Apple subtitles, and it separates itself from the MG bold black titles, not competing for attention; the old version using Noto 700 for the entire line looked like stickers. PingFang SC is a macOS system font, not packaged via @fontsource — rendering works fine on this machine; on Windows/Linux environments, it will fall back to the packaged Noto Sans SC 400 thin variant (already imported in fonts.ts, similar but not identical); before cross-machine rendering, render one frame to confirm subtitle appearance. Subtitle English lines must use proportional Western fonts, see Mistake #42.
  • Large conclusions must always use solid semantic color + textShadow; backgroundClip: text gradient text is forbidden (Mistake #06); GRADIENT is only for color block/stroke purposes.
  • Font sizes only use the SIZE ladder (kicker / subSmall / chip / subZh / subEn / card / h2 / h1 / mega); font sizes outside the ladder are forbidden; 1080p baseline, 4K output scales ×2.
  • Spacing always multiples of GRID (8px); border radius uses RADIUS; safe zones use SAFE (side label anchor points, stack left edge, subtitle distance from bottom; permanent cards should not exceed x55%, person forbidden zone/right zone coordinates in layout.md; subtitle forbidden zone y>82%, left-zone element bottom edge ≤y840 is a stricter hard line than y82% for the left zone).

2. Section SideLabel (permanent throughout the episode, highest priority component)

  • Structure: │ (4px vertical line, semantic color) + ENGLISH KICKER · Chinese phrase (first line, English in kicker specification + semantic color, Chinese white 34px) + second line gray 24px supplementary description.
  • Position: Fixed SAFE.sideLabel; when switching, old label fades out over 12 frames, new label slides in from left over 15 frames.
  • Naming with numbering system: SCENE 01, PATH 1/2/3, positive-negative contrast (DISSOLVED·01.. vs IMMUNE·01..).
  • Color = emotional semantics of this segment, see tokens comments. Reference: refs/01-typography-and-side-labels/.

3. Component Specification Quick Reference (detailed specifications will be added as implementation progresses; first look at reference images, then write)

ComponentKey SpecificationsReference
BilingualSub52 white + black shadow / 28 pure white English, bottom-centered, always on topmost layerany refs
HeroTextkicker + Chinese h1/h2, highlight word in semantic color; variants: red strikethrough correction, yellow brush underline, angled stamprefs/02
ChipBlack card RADIUS.chip + colored icon + 34px text, one color-change word allowedrefs/03
ChecklistChip vertical stack, pop in one by one; inactive item opacity 0.35refs/03
InfoCardicon + kicker + 44px title + blue highlight wordrefs/01
StepList①②③ circle numbers + Chinese + small English kicker, step-by-step illuminationrefs/04
FlowChainBlack card + → arrow chain, nodes appear sequentiallyrefs/04
CompareCardlogo + name + red × weak point chip + green ✓ strong point chip, vertical stackrefs/04
FormulaLarge A symbol B (>, », ⇄, =), symbol in semantic colorrefs/05
BigNumberInter large number + small unit text + kicker; counter uses spring scrollrefs/05
BarChartData bar 8px border radius; key bar turns yellow + crown chip on top; rest grayrefs/05
CurveOverlay2–3px yellow curve overlaid directly on B-roll, right endpoint annotatedrefs/05
TimelineTwo-endpoint interval type / event with red/green result chiprefs/05
ScoreBoardGreen:red large number score comparisonrefs/05
PhoneMockupiPhone frame + gradient stroke glow + embedded screenshot long image scrollingrefs/06
WindowCardMac traffic lights + app icon + feature chip, dynamic contentrefs/04
QuoteDocWhite background screenshot card + yellow fluorescent highlight + black background Chinese translation bar overlayrefs/07
TweetCardAvatar + identity kicker + red large conclusion + views chiprefs/07
PersonCardRound avatar + name + small identity text + institution chiprefs/07
StampTilt -8°~-6° stroke stamp text (red/green), stamp animation = scale 1.3→1 + slight shakerefs/08
MatrixIconBlock matrix icon: red = will be dissolved camp / green = immune camprefs/08
CardWallWhite small cards 3→20 gradually fill the screen, simulating demand explosionrefs/06
BeforeAfterTwo icon cards + blue arrow + conclusion chip belowrefs/04
BreatheIdle micro-motion wrapper: breathing scale ±0.6% + floating ±3px, phase offset to prevent sync; wraps the main elementsrc/design/Breathe.tsx
BrickWallStaggered gold brick horizontal wall built brick by brick + center label, metaphor for "building walls/hard barriers"src/design/BrickWall.tsx
UnitMatrixN sets of block matrices lighting up cell by cell; fillRatio waffle mode (73% data field)src/design/UnitMatrix.tsx
CloneCascadeSource icon card + fakes sequentially fading in + warning chip, metaphor for "being copied/distilled"src/design/CloneCascade.tsx

Batch Scene Inline Components (2026-07-17 batch production additions, not yet promoted to shared components)

The following components currently live only in a single Scene, marked as "Scene inline; when reused a second time, promote to src/design shared component and add to specs table" — when encountering a similar requirement in a new episode, check here first, don't rebuild:

Inline ComponentLocationWhat It DoesPromotion Condition
DissolveGridscenes/Scene08.tsxDissolve grid: the camp that will be dissolved, blocks dissolve one by one, center ⚡ background fades out leaving white outline floatingWhen "camp dissolve" metaphor is reused a second time
VerdictBox (judgment box)scenes/Scene17.tsxRed-green mirror judgment box: ≈400×170 stroke empty box first appears + header inside box + horizontal row of 3 chips entering the box in batches (red fills first, green later, creating a "which side are you on" suspense)When "VS final judgment" is upgraded a second time
NamePlate + CtaBadge (CTA badge row)scenes/Scene19.tsxEnd credits CTA bottom horizontal row: name plate lands first as anchor → four action badges pop in one by one (comment badge widest at the end = emphasis)When ending CTA wrap-up is used a second time

Icon Specifications

  • Icons must always use lucide-react linear SVG icons, color = semantic color (COLOR[accent]), strokeWidth 2.5 approximately; emoji are forbidden (colored rounded blocks conflict with the benchmark's monochrome linear style, lesson from 2026-07 third batch acceptance).
  • The icon prop of Chip / Checklist / SideLabel accepts ReactNode: icon={<Lock size={26} strokeWidth={2.5} />}.

Texture Specifications → references/texture.md

Jelly block gradients, same-color glow, vignette, real logo, SURFACE light/dark variants, stamp specification single line — all in texture.md (as of 2026-07-16, it is the single source of truth for texture; the old text in this section has been migrated).

4. Motion Rules → references/motion.md

useEnter/useExit, beat-by-beat entry, service interval, Breathe, chapter clean screens, forbidden patterns — all in motion.md (as of 2026-07-16, it is the single source of truth for motion; the old text in this section has been migrated; note that the old rule "old info stays with reduced opacity" has been replaced by "service interval + exit discipline").

5. Pre-export Self-Check (run before every render)

  1. Grep check for any inline color values/font sizes/fonts in components beyond tokens.
  2. All permanent cards should not cross faceZone / subtitleZone.
  3. Each title highlight word ≤ 1; English kicker must be uppercase with letter spacing.
  4. Capture 3–5 keyframes and compare side-by-side with corresponding reference images in refs/; if they don't match, identify which token is off, change the token, not inline component values.

6. Mistake Archive

#01 Chinese silently reverts to Songti (2026-07-15 · Design System Day 1)

  • Symptom: Rendered/screenshotted Chinese text becomes Songti, English remains normal; sometimes the same component is normal, sometimes not (depends on luck).
  • Cause: Three layers, the last is the real culprit. ① @remotion/google-fonts pulls fonts from gstatic at runtime; domestic network cannot reach, silent fallback; ② Chinese webfont is split by unicode-range into packages; loading has race conditions with screenshot; ③ Tree-shaking: sideEffects: ["*.css"] in package.json causes fonts.ts (which is only imported as a side effect) to be completely removed from the production bundle; the font CSS is not in the rendering bundle at all (document.fonts.size === 0) — Studio dev mode does not tree-shake, so "Studio is fine, render is broken".
  • Fix: ① Fonts use @fontsource local packages (src/design/fonts.ts); ② sideEffects in package.json must include "src/design/fonts.ts"; ③ Any composition containing text must have <FontGuard/>: load→check loop verification, and must also check document.fonts.size > 0 (fonts.check() returns true vacuously when zero fonts are registered); on timeout, call cancelRender to report error, never silently output a Songti frame.
  • Lesson: When "Studio normal, export broken", first suspect production build differences (tree-shaking/minification), not mysterious race conditions; font verification must rely on FontGuard as a hard gate, human eyes cannot be trusted with thumbnails.

#02 remotion still output ○ = reusing old image

  • Symptom: After code changes, rerender output looks unchanged; command line shows ○ instead of +.
  • Fix: Before verifying render, delete old output (or render to a new filename); if necessary, add --bundle-cache=false. + = true render, ○ = suspicious.
  • Lesson: Verification against output must be based on confirmed fresh render products; otherwise, you will misdiagnose "fix not working" (wasted two rounds because of this).

#03 Thumbnail comparison images can be deceptive

  • Symptom: In a side-by-side small image, Chinese text looks like Songti, but the full-resolution original is actually normal.
  • Fix: When judging font/stroke details, crop the original resolution and zoom in locally; do not use entire frame thumbnails to draw conclusions.

#04 JSX attribute strings do not parse \uXXXX escapes

  • Symptom: <Chip icon="\u2705" /> displays the literal \u2705 on screen instead of ✅.
  • Cause: The string literal of a JSX attribute is HTML-style text, not processed by JS escape parsing; only strings in JS expressions (icon={'\u2705'}) are parsed for escapes. Most commonly hit when using scripts to batch write TSX.
  • Fix: Emoji/special characters in JSX attributes should use the real character directly (icon="✅"); if escapes are truly needed, write icon={'\u2705'}.

#06 backgroundClip: text gradient text renders dark

  • Symptom: Large titles using backgroundImage gradient + WebkitBackgroundClip:'text' + WebkitTextFillColor:'transparent' look okay in Studio, but rendered output colors come out dark and dull (bright red becomes brick red), rejected by user at first glance.
  • Fix: Large conclusions always use solid semantic color COLOR[semantic] + textShadow; for depth, rely on shadows and font weight, not gradient fills. tokens.GRADIENT is only for color block/stroke purposes.

#08 Final MG text edges have halos, smudging ("MG makes video quality worse") (2026-07-16 · 0715-01 v3)

  • Symptom: User says rendered video quality is poor; when zoomed in, MG text edges show JPEG mosquito noise/halos, more visible on sharper elements. Original footage at 15 Mbps HEVC is very clean.
  • Cause: Remotion's default pipeline has double compression — each frame is saved as JPEG at quality 80, then compressed to H264 at CRF 18. Large MG text/thin lines are high-frequency signals, first to show compression artifacts. Before adding MG, artifacts hide in real footage texture and are not noticeable, so they are misdiagnosed as "MG causing poor quality".
  • Fix: In remotion.config.ts, freeze Config.setJpegQuality(100) + Config.setCrf(15) (already set in this project, do not revert). Volume increases ~20%, rendering slightly slower, worth it.
  • Diagnosis Method: For the same frame, capture one from source/old final/new test parameter segment; crop the same MG text area, zoom in, and compare side by side; first render a 5-second test segment to verify parameters, then render the full episode.

#09 Long render disappears midway, mp4 becomes corrupted fragment (2026-07-16 · 0715-01 v3)

  • Symptom: After rendering for a long time, the process is still running but output file does not grow; or rendering silently disappears, leaving an unopenable mp4.
  • Cause: ① Render is attached to a child process of an AI session; when the session restarts/exits, it kills the render (current case: killed at 90%); ② Checking process with pgrep -f "remotion render" matches the check script itself (command line contains the same string), creating the illusion that "process is still alive".
  • Fix: ① Renders longer than a few minutes must always use nohup npx remotion render … > /tmp/xx.log 2>&1 & disown to detach from the session; ② Completion criterion should look for the + output filename line in the log (+ = true render success), not process existence; ③ Process checking must exclude self-match (look at CPU usage or use exact PID); ④ Delete corrupted fragment before rerendering.
  • Lesson: Never deliver a corrupted mp4 fragment (duration/size mismatched); always verify duration and audio track with ffprobe before showing to the user.

#42 Subtitle English line must use proportional font, avoiding monospace width breaking character spacing

  • Symptom: Subtitle English line looks uneven and has inconsistent character spacing; if a monospace font is used, each character takes the same width, causing spacing imbalances.
  • Fix: Ensure subtitle English line uses FONT.subEn (Inter 300), which is a proportional Western font. If using another font, verify it is proportional.
  • Lesson: Subtitle English must use a proportional font; otherwise, character spacing, especially in long text, will look odd, breaking the subtitle's visual rhythm.

Individual skills in this repo

This repo contains 5 individual skills — each has its own dedicated page.

Related Skills