Skip to main content
Glama
COMPONENT_DESIGN.md10.7 kB
# Component Design Principles ## Overview This document describes the architectural patterns and design principles for all Remotion components in the chuk-motion generator system. ## Component Architecture ### Component Types Components are organized into three categories: 1. **Overlays** (`overlays/`) - UI elements that appear on top of content - Title cards, lower thirds, text overlays - Examples: TitleScene, LowerThird 2. **Content** (`content/`) - Primary visual content elements - Code displays, charts, visualizations, demo boxes - Examples: CodeBlock, TypingCode, LineChart, DemoBox 3. **Layouts** (`layouts/`) - Container components that arrange children - Grid systems, split screens, multi-cam setups - Examples: Grid, ThreeByThreeGrid, AsymmetricLayout, PiPLayout ## Core Design Patterns ### 1. Timing Props (Required) **Every component MUST accept these timing props:** ```typescript interface ComponentProps { startFrame: number; // When component becomes visible durationInFrames: number; // How long component is visible // ... other props } ``` **Visibility Check Pattern:** ```typescript export const Component: React.FC<ComponentProps> = ({ startFrame, durationInFrames, // ... other props }) => { const frame = useCurrentFrame(); // Don't render if outside the time range if (frame < startFrame || frame >= startFrame + durationInFrames) { return null; } // Component implementation }; ``` **Why?** This enables precise frame-based timing control for video composition. ### 2. Props vs Template Variables Components use a **two-phase rendering system**: #### Phase 1: Template-Time (Jinja2) - **Design tokens** injected from theme - **Configuration values** from scene config - Uses `[[ variable ]]` and `[% logic %]` syntax ```typescript // Template variables (resolved at generation time) const color = '[[ colors.primary[0] ]]'; // → '#0066FF' const font = '[[ typography.primary_font.fonts ]]'; // → 'Inter' const damping = [[ motion.default_spring.config.damping ]]; // → 200 ``` #### Phase 2: Runtime (React/Remotion) - **Content data** passed as props - **Dynamic behavior** calculated per-frame - Standard TypeScript/React props ```typescript // Runtime props (resolved during video rendering) interface ComponentProps { title: string; // Content data variant?: string; // Behavior control startFrame: number; // Timing durationInFrames: number; // Timing } ``` ### 3. Children Handling #### Named Props Pattern (Preferred) Use named props for components with specific, semantic children: ```typescript interface LayoutProps { leftPanel?: React.ReactNode; rightPanel?: React.ReactNode; startFrame: number; durationInFrames: number; } export const SplitScreen: React.FC<LayoutProps> = ({ leftPanel, rightPanel, // ... }) => { return ( <AbsoluteFill> <div style={{ flex: 1 }}>{leftPanel}</div> <div style={{ flex: 1 }}>{rightPanel}</div> </AbsoluteFill> ); }; ``` **Use named props when:** - Children have specific semantic meaning (left/right, main/pip, host/screen) - Different children need different styling or positioning - Component layout is not generic #### Children Array Pattern Use children array for generic, homogeneous layouts: ```typescript interface GridProps { children: React.ReactNode[]; startFrame: number; durationInFrames: number; // ... } export const Grid: React.FC<GridProps> = ({ children, // ... }) => { const gridChildren = Array.isArray(children) ? children : [children]; return ( <div style={{ display: 'grid' }}> {gridChildren.map((child, idx) => ( <div key={idx}>{child}</div> ))} </div> ); }; ``` **Use children array when:** - All children are treated equally - Number of children is variable - Generic grid or list layouts ### 4. Styling Patterns #### Absolute Positioning Layout components should use `AbsoluteFill` for full-screen layouts: ```typescript return ( <AbsoluteFill style={{ pointerEvents: 'none' }}> {/* Layout content */} </AbsoluteFill> ); ``` #### Relative Positioning Content within layouts should use relative positioning with flex or grid: ```typescript <div style={{ position: 'absolute', top: padding, left: padding, right: padding, bottom: padding, display: 'flex', gap: gap, }} > {/* Content */} </div> ``` ### 5. Animation Patterns #### Spring Animations (Entrances) Use springs for smooth, natural entrances: ```typescript const entranceProgress = spring({ frame: relativeFrame, fps, config: { damping: [[ motion.default_spring.config.damping ]], stiffness: [[ motion.default_spring.config.stiffness ]] } }); const scale = interpolate(entranceProgress, [0, 1], [0.8, 1]); const opacity = entranceProgress; ``` #### Interpolate (Exits and Linear) Use interpolate for exits and linear transitions: ```typescript const exitDuration = 20; const exitProgress = interpolate( relativeFrame, [durationInFrames - exitDuration, durationInFrames], [1, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } ); const finalOpacity = entranceOpacity * exitProgress; ``` ### 6. Variant System Components should support multiple visual variants: ```typescript interface ComponentProps { variant?: string; // 'minimal' | 'standard' | 'bold' | 'glass' // ... } const variants = { minimal: { /* styles */ }, standard: { /* styles */ }, bold: { /* styles */ }, glass: { /* styles */ } }; const variantStyle = variants[variant as keyof typeof variants] || variants.standard; ``` ### 7. Design Token Integration Always use design tokens for theming: ```typescript // ✅ CORRECT - Uses theme tokens const color = '[[ colors.primary[0] ]]'; const font = '[[ typography.primary_font.fonts ]]'; const background = '[[ colors.background.dark ]]'; // ❌ WRONG - Hardcoded values const color = '#0066FF'; const font = 'Inter'; const background = '#0A0E1A'; ``` ## Component Structure Template Every component should follow this structure: ```typescript import React from 'react'; import { AbsoluteFill, useCurrentFrame, useVideoConfig } from 'remotion'; interface ComponentNameProps { // Content props title?: string; // Timing props (REQUIRED) startFrame: number; durationInFrames: number; // Styling props variant?: string; animation?: string; // Configuration props padding?: number; gap?: number; } /** * ComponentName - Brief description * * Perfect for: * - Use case 1 * - Use case 2 * * Features: * - Feature 1 * - Feature 2 */ export const ComponentName: React.FC<ComponentNameProps> = ({ title = 'Default Title', startFrame, durationInFrames, variant = 'standard', animation = 'fade_in', padding = 40, gap = 20 }) => { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); const relativeFrame = frame - startFrame; // Visibility check if (frame < startFrame || frame >= startFrame + durationInFrames) { return null; } // Animation logic const entranceProgress = spring({ frame: relativeFrame, fps, config: { damping: [[ motion.default_spring.config.damping ]] } }); // Variant styles const variants = { standard: { /* ... */ }, bold: { /* ... */ } }; const variantStyle = variants[variant as keyof typeof variants] || variants.standard; // Render return ( <AbsoluteFill style={{ pointerEvents: 'none' }}> {/* Component implementation */} </AbsoluteFill> ); }; ``` ## Best Practices ### DO ✅ 1. **Always include timing props** (startFrame, durationInFrames) 2. **Always include visibility check** at the start of the component 3. **Use design tokens** for colors, fonts, and motion 4. **Provide default values** for all optional props 5. **Use semantic prop names** (hostView, screenContent, not just child1, child2) 6. **Include JSDoc comments** describing the component's purpose 7. **Support multiple variants** for flexibility 8. **Use spring animations** for entrances 9. **Use interpolate** for exits and linear animations 10. **Test with multiple themes** to ensure token system works ### DON'T ❌ 1. **Don't hardcode colors or fonts** - use theme tokens 2. **Don't skip visibility checks** - components should respect timing 3. **Don't use generic prop names** like "content" when semantic names fit 4. **Don't forget exit animations** - fade out at end of duration 5. **Don't use state** unless absolutely necessary (prefer frame calculations) 6. **Don't rely on external data** during render (pre-load everything) 7. **Don't nest layouts deeply** - keep component hierarchy flat 8. **Don't use percentages** without calc() for gaps/padding 9. **Don't forget TypeScript interfaces** - type all props 10. **Don't skip tests** - every component should have comprehensive tests ## Testing Requirements Every component must have tests covering: 1. **Basic generation** - Component renders without errors 2. **Timing props** - startFrame and durationInFrames are present 3. **Visibility check** - Component respects frame timing 4. **TypeScript validity** - No unresolved template variables 5. **Interface definition** - Proper TypeScript interface exists 6. **Variants** (if applicable) - All variants generate correctly 7. **Design tokens** - Theme colors/fonts/motion are injected 8. **Props** - All props are properly used See `tests/templates/README_TEMPLATE_TESTS.md` for testing guidelines. ## File Organization ``` src/chuk_motion/generator/templates/ ├── overlays/ # UI overlay components │ ├── TitleScene.tsx.j2 │ └── LowerThird.tsx.j2 ├── content/ # Content display components │ ├── CodeBlock.tsx.j2 │ ├── TypingCode.tsx.j2 │ ├── LineChart.tsx.j2 │ └── DemoBox.tsx.j2 └── layouts/ # Container layout components ├── Grid.tsx.j2 ├── ThreeByThreeGrid.tsx.j2 ├── AsymmetricLayout.tsx.j2 └── ... (more layouts) ``` ## Documentation Structure Each component should have its own documentation file: ``` docs/components/ ├── COMPONENT_DESIGN.md # This file ├── overlays/ │ ├── TitleScene.md │ └── LowerThird.md ├── content/ │ ├── CodeBlock.md │ ├── TypingCode.md │ ├── LineChart.md │ └── DemoBox.md └── layouts/ ├── Grid.md ├── ThreeByThreeGrid.md └── ... (one per layout) ``` See individual component documentation for detailed usage examples and configuration options.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/chrishayuk/chuk-mcp-remotion'

If you have feedback or need assistance with the MCP directory API, please join our Discord server