Communitygithub.com

AlexGit92s/Remotion2.0

Estruture a marcação Remotion para tornar componentes interativos no Remotion Studio.

O que é Remotion2.0?

Este skill define um conjunto de práticas e padrões para estruturar a marcação (markup) Remotion de forma que o Remotion Studio possa reconhecer a estrutura do código e torná-la interativa. Ele permite que elementos sejam selecionados com um clique, arrastados, redimensionados, rotacionados, e que estilos CSS, keyframes e easing sejam editados visualmente. Se a marcação for muito complexa, os valores ficam desabilitados. O skill é útil para desenvolvedores que desejam criar componentes visuais (como cartões, títulos e cenas) cujas props possam ser editadas por instância no Studio, sem precisar modificar o código-fonte diretamente. Ele suporta o ecossistema Remotion e a API `Interactive`, com a funcionalidade `wrapInSequence` integrada. Através de diretrizes claras, o skill ensina a usar `Interactive.withSchema`, preferir componentes interativos com suas próprias timelines, evitar sequências redundantes, e manter estilos CSS inline para máxima editabilidade.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/AlexGit92s/Remotion2.0/tree/HEAD/.claude/skills/remotion-interactivity

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Prévia

Do README da skill

Remotion2.0 preview

Documentação

O que Remotion2.0 faz?

Ao escrever a marcação Remotion de uma maneira específica, o Remotion Studio consegue reconhecer a estrutura do código e a torna interativa:

  • Permitindo que itens sejam selecionados clicando neles
  • Permitindo arrastar e soltar, redimensionar e rotacionar
  • Editando os estilos CSS
  • Tornando keyframes e valores de easing editáveis

Se a marcação for muito complexa para o Studio tornar interativa, os valores ficam acinzentados.

Prefira componentes interativos com suas próprias timelines

Use Interactive.withSchema({wrapInSequence: true}) para cenas personalizadas, cartões, títulos e outros componentes visuais cujas props devem ser editáveis por instância. Exponha controles de conteúdo e aparência significativos em um InteractivitySchema. Mantenha detalhes decorativos de implementação dentro do componente.

O componente deve aceitar style e repassá-lo a uma raiz visual. Mantenha uma raiz compartilhada quando transformações, corte ou opacidade devem afetar todas as suas camadas. Achate wrappers internos redundantes quando seus estilos podem ser movidos para um elemento existente sem alterar layout ou animação.

import type React from 'react';
import {Interactive, type InteractivitySchema} from 'remotion';

type LowerThirdProps = {
  readonly children: string;
  readonly accentColor: string;
  readonly style?: React.CSSProperties;
};

const LowerThirdInner: React.FC<LowerThirdProps> = ({
  children,
  accentColor,
  style,
}) => {
  return (
    <Interactive.Div
      style={{
        position: 'absolute',
        left: 80,
        bottom: 80,
        display: 'flex',
        alignItems: 'center',
        gap: 20,
        backgroundColor: 'white',
        borderRadius: 16,
        padding: '20px 32px',
        color: 'black',
        fontFamily: 'Helvetica, Arial, sans-serif',
        fontSize: 48,
        fontWeight: 600,
        ...style,
      }}
    >
      <div
        style={{
          width: 8,
          alignSelf: 'stretch',
          borderRadius: 4,
          backgroundColor: accentColor,
        }}
      />
      {children}
    </Interactive.Div>
  );
};

const lowerThirdSchema = {
  children: {type: 'text-content', default: '', description: 'Texto'},
  accentColor: {
    type: 'color',
    default: '#0b84f3',
    description: 'Cor de destaque',
  },
} as const satisfies InteractivitySchema;

export const LowerThird = Interactive.withSchema({
  Component: LowerThirdInner,
  componentName: '<LowerThird>',
  schema: lowerThirdSchema,
  wrapInSequence: true,
});

Reenviar o style injetado dentro do componente é obrigatório; mantenha os estilos editáveis inline no local de chamada do componente.

Escolha campos de schema

Cada chave no schema é uma prop que o Studio pode ler e editar no local de chamada. Props que não estão no schema ainda funcionam, mas não são editáveis no Studio. Chaves podem usar notação de ponto, como style.color.

TipoUso paraCom keyframe
text-contentTexto, como um títuloNão
numberNúmeros, com min, max, step, integer opcionaisSim
booleanInterruptores liga/desligaSim (segurar)
colorStrings de cor CSSSim
enumUma escolha entre variants, cada uma com seu próprio schemaOpcional (segurar)
arrayListas de números, cores, enums e maisNão
assetFontes de mídia, com assetType opcionalNão
font-familyFamília de fonte CSSNão
font-weightPeso da fonteSim
translate, scale, rotation-css, rotation-degrees, transform-originTransformaçõesSim
uv-coordinateUm ponto normalizado [x, y] no elementoSim
svg-pathDados de caminho SVGSim
remotion-captionsDados Caption[]Não
hiddenUm valor mantido fora dos controles—

Todo campo precisa de um default e deve ter um description, que o Studio mostra como rótulo. Defina keyframable: false para exibir um controle estático.

Exponha conteúdo e aparência por instância, como o texto e uma cor de destaque. Não adicione campos para style.translate, style.scale, style.rotate, style.transformOrigin e style.opacity: wrapInSequence: true já os adiciona.

Para tornar propriedades CSS da raiz editáveis por instância, espalhe os fragmentos de schema embutidos:

const cardSchema = {
  ...Interactive.textSchema, // style.color, style.fontSize, style.fontWeight, ...
  ...Interactive.backgroundSchema, // style.backgroundColor
  ...Interactive.borderRadiusSchema, // style.borderRadius, ...
} as const satisfies InteractivitySchema;

Também disponível: Interactive.borderSchema, e Interactive.captionsSchema para componentes que aceitam legendas. Consulte InteractivitySchema para todas as opções.

Registre componentes reutilizáveis como composições conectadas

Registre cenas substanciais e componentes reutilizáveis com suas próprias camadas ou animação como composições independentes. O Studio chama essas composições conectadas: elas podem ser abertas em sua própria timeline enquanto compartilham a mesma implementação de componente com o vídeo pai.

Defina estilos necessários e carregue fontes dentro do componente para que funcione sem seu pai. Consulte independência do pai.

Registre a mesma referência de componente exportada que o pai renderiza. Para o exemplo acima, use component={LowerThird}, não LowerThirdInner ou um wrapper inline. Com wrapInSequence: true, nenhum <Sequence> extra é necessário para a conexão.

import {Composition} from 'remotion';
import {LowerThird} from './LowerThird';

export const RemotionRoot = () => (
  <Composition
    id="LowerThird"
    component={LowerThird}
    width={1280}
    height={720}
    fps={30}
    durationInFrames={30}
    defaultProps={{
      children: 'Jane Doe, Product Designer',
      accentColor: '#0b84f3',
    }}
  />
);

Escolha um ID único, valores padrão inline representativos e dimensões, fps e duração adequados para pré-visualizar o componente. Os padrões de registro não copiam automaticamente props de uma instância pai. Consulte composições conectadas para detalhes de registro e extração.

Coloque o tempo diretamente no componente

Evite um <Sequence> separado em torno de um componente que já aceita props de tempo. Componentes interativos embutidos e componentes personalizados feitos com wrapInSequence: true podem receber name, from, durationInFrames, trimBefore, playbackRate e premountFor diretamente.

<LowerThird
  name="Lower third"
  from={30}
  durationInFrames={90}
  accentColor="#0b84f3"
>
  Jane Doe, Product Designer
</LowerThird>

Ao remover uma sequência redundante, mova seu tempo e nome para o filho. Preserve qualquer tempo filho existente; não sobrescreva ou aplique offsets duplicados. Uma chamada useCurrentFrame() dentro do componente lê seu quadro local, enquanto uma expressão de estilo no local de chamada ainda usa o quadro do chamador. Preserve essa distinção ao mover estilos de animação.

Mantenha um <Sequence> quando ele fornecer tempo compartilhado para vários irmãos, substituir dimensões para useVideoConfig(), ou envolver um componente que não lida com tempo. Mantenha <Series.Sequence> para layout consecutivo e <TransitionSeries.Sequence> para transições; props from diretas não substituem esses comportamentos.

Dê a cada item editável individual seu próprio nó JSX

O Studio edita o nó fonte JSX que criou um item. Se vários itens em tempo de execução vêm do mesmo nó JSX, eles compartilham um alvo de edição de fonte.

Para cada registro de composição, clipe, cena, camada ou sequência que deve ser editável por si só, escreva um nó JSX separado e mantenha suas props editáveis nesse nó. Isso se aplica a <Composition>, <Still>, componentes de mídia embutidos, <Sequence>, <Series.Sequence>, <TransitionSeries.Sequence> e componentes personalizados.

Por exemplo, crie uma timeline editável assim:

<Series>
  <Series.Sequence name="Introdução" durationInFrames={90}>
    <Introducao />
  </Series.Sequence>
  <Series.Sequence name="Demonstração" durationInFrames={150}>
    <Demonstracao />
  </Series.Sequence>
</Series>

Um .map() ou outro loop programático criaria vários itens em tempo de execução a partir de um nó JSX fonte. Isso é apropriado para saída repetida que é intencionalmente controlada como um modelo, como barras de visualização ou partículas. Não é apropriado quando as instâncias precisam de IDs ou nomes, props, metadados, tempo, ordenação, exclusão ou duplicação independentes no Studio.

Torne um elemento HTML interativo usando Interactive

Todo elemento HTML e SVG (exceto <Img>, que já é interativo), como <div>, pode ser tornado interativo usando Interactive:

<Interactive.Div
  name="Cartão de saudação"
  style={{fontSize: 80, padding: 24}}
>
  Olá
</Interactive.Div>

Isso permite que estilos e keyframes sejam definidos no Studio. Seja sensato; se um componente tiver muitos elementos, a timeline pode ficar bagunçada.

Prefira texto inline

Se o texto é fixo e usado apenas uma vez, escreva-o diretamente dentro do elemento interativo em vez de extraí-lo para uma constante.

// 👍 Cópia fixa permanece editável
<Interactive.Div name="Título">
  Melhores Práticas Remotion
</Interactive.Div>

Use uma prop ou variável apenas quando o texto é dinâmico ou reutilizado.

Dê nomes descritivos aos elementos interativos

Adicione uma prop name aos elementos para torná-los facilmente identificáveis. Evite nomes computados; codifique-os.

<>
  <Interactive.Div name="Título principal" style={{fontSize: 80}}>
    Dia de lançamento
  </Interactive.Div>
  <Img name="Avatar" src="https://remotion.media/image.jpeg" />
  <Video name="Background" src="https://remotion.media/video.mp4" />
  <Interactive.Div name="Título">
    Dia de lançamento
  </Interactive.Div>
</>

Mantenha todos os estilos CSS inline

A melhor maneira é apenas passar um objeto simples para style - sem referência a constantes, sem espalhamento de objetos, sem matemática.

<Interactive.Div
  style={{
    fontSize: 80,
    color: 'red',
  }}
>
  Olá Mundo!
</Interactive.Div>
const baseStyle = useMemo(() => {
  return {
    fontSize: 12 // ❌ Estilos não inline não são suportados
  }
}, []);

<Interactive.Div
  style={{
    ...baseStyle, // ❌ Espalhamento não é suportado
    color: RED, // ❌ Referência a constantes não é suportada
    scale: frame * 10 // ❌ Matemática não é suportada
  }}
>
  Olá Mundo!
</Interactive.Div>

Anime usando interpolate()

Escreva animações como chamadas interpolate() inline na propriedade que muda. O intervalo de saída, easing, extrapolação e a propriedade output devem usar valores codificados.

O intervalo de entrada pode adicionalmente usar durationInFrames, fps, width e height desestruturados diretamente de useVideoConfig(). Identificadores simples como durationInFrames, multiplicação por um número como 2 * fps ou fps * 2, e subtração de um número como durationInFrames - 1 são suportados.

const {fps, durationInFrames} = useVideoConfig();

// 👍 Valores inline podem ser padronizados e ter keyframes
<Interactive.Div
  name="Cartão de produto"
  style={{
    color: 'white',
    fontSize: 80,
    scale: interpolate(frame, [0, fps], [0, 1], {
      easing: Easing.spring({damping: 200}),
      output: 'perceptual-scale',
      extrapolateLeft: 'clamp',
      extrapolateRight: 'clamp'
    }),
    rotate: interpolate(frame, [0, 1 * fps], ['0deg', '20deg'], {
      easing: Easing.spring({damping: 200}),
      extrapolateLeft: 'clamp',
      extrapolateRight: 'clamp'
    }),
    translate: interpolate(
      frame,
      [durationInFrames - 30, durationInFrames],
      ['0px 0px', '0px 120px'],
      {
        easing: Easing.spring({damping: 200}),
        output: 'perceptual-scale',
        extrapolateLeft: 'clamp',
        extrapolateRight: 'clamp'
      }
    ),
  }}
/>
const translateY = interpolate(frame, [0, 30], [0, 120]); // ❌ Matemática deve estar diretamente na marcação

<Interactive.Div
  name="Cartão de produto"
  style={{
    translate: translateY, // ❌ Apenas chamadas interpolate() inline são suportadas
    rotate: interpolate(frame, [start, start + 10], [0, Math.PI]), // ❌ Não pode usar matemática com variáveis arbitrárias, não pode usar constantes
    scale: interpolate(frame, [0, 1], [0, 1]), // ❌ 'output' deve ser 'perceptual-scale' para transformações de escala ou translate
  }}
>
  Olá Mundo!
</Interactive.Div>

Prefira keyframes em vez de condições

Evite expressões ternárias e condicionais. Em vez disso, use interpolate() para gerar animações contínuas, mesmo para alternância de visibilidade:

// 👍 Interpolate é editável
<Interactive.Div
  style={{
    opacity: interpolate(frame, [0, 1], [1, 0], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}),
  }}
>
  Conteúdo
</Interactive.Div>
<Interactive.Div
  style={{
    opacity: frame > 10 ? 0 : 1, // ❌ Não pode ser editado como keyframe
  }}
>
  Conteúdo
</Interactive.Div>

Estrutura do componente para editabilidade ideal

Siga este padrão para componentes com props expostas ao usuário no Studio:

  1. Defina um schema de interatividade tipado com satisfies InteractivitySchema.
  2. Exporte o componente gerado por Interactive.withSchema({...}).
  3. (Opcional, para comportamento personalizado) Use Interactive.useInteractivity().
  4. Aceite style?: React.CSSProperties no componente interno e aplique-o à raiz visual.
  5. Passe props restantes diretamente para os elementos filho conforme apropriado.

Suporte a layouts complexos

Componentes com layouts complexos ou que usam Suspense ou dados assíncronos podem precisar de manipulação especial para funcionar no Studio.

Consulte as Diretrizes de layout para obter mais orientações.

Lembre-se

Sempre que possível, torne os componentes editáveis no Studio usando as técnicas acima.

Individual skills in this repo

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

Habilidades Relacionadas