Communitygithub.com

og-image-animated

프로젝트의 Open Graph 공유 카드를 몇 초 분량의 웹 애니메이션으로 변환합니다. 브랜드 마크와 제목이 위에서 떨어지고, 실제 앱이 아래에서 올라와 최종 카드에 자리 잡습니다. 현재 스택에 재생 가능한 웹 라우트를 만들고 프레임별로 녹화하여 MP4, GIF(및 마지막 프레임은 PNG)로 출력합니다.

og-image-animated란 무엇인가요?

이 스킬은 정적 OG 이미지 카드에 부드러운 애니메이션을 추가하여 소셜 미디어, README, 런칭 페이지 등에서 사용할 수 있는 짧은 동영상/GIF를 생성합니다. 핵심은 기존 카드 컴포넌트에 `animated` prop을 추가하여 정적 PNG와 애니메이션이 항상 동기화되도록 하는 것입니다. 타이밍은 약 2~2.5초이며, CSS 애니메이션만 사용하고(JS 트윈 금지) opacity와 transform(translateY) 효과만을 cubic-bezier(0.16, 1, 0.3, 1) 이징 커브로 적용합니다. Playwright와 ffmpeg를 사용하여 프레임을 녹화하며, 결과물은 MP4(2400×1260), GIF(1200px), PNG(최종 프레임)입니다. `record.mjs` 스크립트가 매번 동일한 결과를 보장하며, `--fps`, `--hold`, `--formats` 등 다양한 옵션을 지원합니다. 애니메이션 라우트는 `/og/<page>-animated`이며, `?loop=0` 파라미터로 한 번 재생(녹화용)하거나 반복 재생(미리보기용)할 수 있습니다. 녹화 중에는 reduced motion 설정이 없어야 하며, 스크립트가 `data-og-frame` 요소의 화면을 프레임별로 캡처합니다.

지원 대상✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/paralect/saas-skills/tree/HEAD/skills/og-image-animated

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

애니메이션 OG 이미지

이 스킬이 사용하는 모든 파일은 이 SKILL.md 바로 옆에 있는 자체 폴더에 있으며, 여기서 사용하는 경로는 모두 해당 폴더를 기준으로 합니다. 다른 스킬이나 폴더에서 가져올 것이 없으니 찾지 마세요. 사용자가 다른 위치를 지정하지 않는 한, 생성된 모든 파일은 현재 프로젝트의 out/og-image-animated/ 아래에 저장합니다.

정적 공유 카드를 짧고 조용한 리빌(Reveal)로 애니메이션화합니다:

  1. 빈 카드: 배경과 부드러운 글로우(glow)만 표시.
  2. 브랜드 마크와 제목이 위에서 떨어짐 (페이드 + 슬라이드 다운).
  3. 앱 창이 아래에서 올라옴 (페이드 + 슬라이드 업) 정적 카드에서 정확히 있던 위치에 안착.
  4. 마지막 프레임에서 정지하며, 정적 OG 이미지와 일치해야 함.

예시: references/pulse.gif (결과물) 및 references/pulse-frames.png (네 프레임: 시작, 제목 들어옴, 앱 상승, 최종).

규칙

  • 정적 카드에서 시작하세요. 프로젝트에 OG 카드 컴포넌트가 아직 없다면, 먼저 references/static-card.md를 따라 빌드하세요 (실제 제품, 현재 사이트의 메시지, 현실적인 데이터). 애니메이션은 움직임만 추가합니다: 동일한 마크업, 동일한 최종 레이아웃, 새로운 콘텐츠 없음.
  • 하나의 컴포넌트, 두 가지 모드. 컴포넌트를 복사하지 말고 기존 카드 컴포넌트에 animated prop을 추가하세요. 그래야 정적 PNG와 애니메이션이 서로 떨어져 나가지 않습니다. prop이 없으면 아무것도 애니메이션되지 않습니다.
  • 움직임이지, 효과가 아닙니다. opacity + transform: translateY만 사용하며, 하나의 공유 이즈 곡선(cubic-bezier(0.16, 1, 0.3, 1))을 사용합니다. 바운스, 블러, 확대 팝, 타자기 텍스트 또는 회전 요소는 없습니다.
  • 짧게. 전체 타임라인 약 2~2.5초, 이후 약 1.5초 정지. 제목은 약 1.3초까지 완전히 읽을 수 있어야 합니다.
  • CSS 애니메이션만 사용 (키프레임 + animation 속성 both fill 사용), 카드 내부에서 JS 기반 트윈이나 타이머 사용 금지. 레코더가 Web Animations API를 통해 CSS 애니메이션을 탐색합니다. JS 기반 움직임은 결정적으로 캡처되지 않습니다.

타임라인

요소키프레임지속 시간지연 시간
브랜드 마크opacity: 0; translateY(-48px)에서 시작900ms100ms
제목opacity: 0; translateY(-48px)에서 시작1000ms250ms
앱 창opacity: 0; translateY(320px)에서 시작1300ms700ms

카드에 맞게 거리를 조정하세요: 앱은 대부분 아래쪽 가장자리 아래(카드 높이의 절반 정도)에서 시작하고, 제목은 정지 위치 위로 자신의 높이만큼 떠 있어야 합니다. 떠 있는 패널이 있다면 앱 시작보다 200ms 늦게 도착하게 하세요.

단계

1. 발견

  • 정적 카드 컴포넌트와 렌더 라우트(예: og-image.tsx, /og/<page>) 및 public/의 현재 PNG를 찾으세요.
  • 브랜드 마크, 제목 및 앱 창 래퍼를 확인하세요. 창이 zoom을 사용하여 맞춰지면, 줌된 요소가 아닌 내부 래퍼를 애니메이션 처리하세요(오프셋이 올바르게 유지되도록).

2. 애니메이션 모드 추가

카드 컴포넌트에서:

const KEYFRAMES = `
@keyframes og-drop { from { opacity: 0; transform: translateY(-48px); } to { opacity: 1; transform: none; } }
@keyframes og-rise { from { opacity: 0; transform: translateY(320px); } to { opacity: 1; transform: none; } }
`;
const EASE = 'cubic-bezier(0.16, 1, 0.3, 1)';
const enter = (name: string, ms: number, delay: number) => ({ animation: `${name} ${ms}ms ${EASE} ${delay}ms both` });

// <div data-og-frame …>{animated && <style>{KEYFRAMES}</style>} …
// 브랜드: style={animated ? enter('og-drop', 900, 100) : undefined}
// 제목:  style={animated ? enter('og-drop', 1000, 250) : undefined}
// 창: <div style={{ zoom }}><div className="h-full" style={animated ? enter('og-rise', 1300, 700) : undefined}>…

키프레임은 컴포넌트(<style> 태그) 또는 프로젝트의 전역 CSS에 넣으세요. Tailwind v4의 경우 @theme 내부의 키프레임은 일치하는 --animate-* 토큰이 사용될 때만 방출되므로, 컴포넌트 또는 일반 CSS를 사용하세요.

3. 재생 웹 라우트 추가

정적 라우트의 형제 경로로, 예: /og/<page>-animated (noindex, 앱 크롬 없음):

  • 1200×630 프레임 내부에 <Card animated /> 렌더링.
  • 미리보기용으로 약 4.5초마다 key를 변경하여 카드를 다시 마운트하여 재생.
  • ?loop=0이면 한 번 재생(레코더가 사용). 검색 파라미터를 먼저 문자열로 정규화하세요. TanStack과 같은 라우터가 ?loop=0을 숫자 0으로 파싱할 수 있으며, 체크를 놓치면 녹화 중에도 카드가 계속 리마운트됩니다.

브라우저에서 열어 여러 번 시청하세요. 진입 순서, 애니메이션 시작 전 깜빡임이 없는지, 최종 상태가 정적 카드와 일치하는지 확인하세요.

4. MP4 + GIF 녹화

개발 서버가 실행 중일 때:

node <이 스킬 폴더>/scripts/record.mjs \
  --url "http://localhost:<port>/og/<page>-animated?loop=0" \
  --out out/og-image-animated/og-<page>

og-<page>.mp4 (2400×1260, H.264, 30fps), og-<page>.gif (1200px 너비, 팔레트 최적화, 반복) 및 og-<page>.png (마지막 프레임)를 생성합니다. 작동 방식: 페이지의 모든 애니메이션을 일시 중지하고, 타임라인 길이를 읽은 다음, 모든 애니메이션을 프레임별로 탐색하고 고정된 [data-og-frame] 클립의 스크린샷을 찍습니다. 결과는 매 실행마다 동일하며, 드롭된 프레임이 없습니다. 옵션: --fps, --hold, --lead, --scale, --formats mp4,gif,png, --selector.

ffmpeg가 PATH에 있어야 합니다. Playwright는 이 스킬의 scripts/ 폴더에 첫 실행 시 설치되며, Chromium이 없으면 함께 설치됩니다.

주의사항:

  • 페이지가 reduced motion을 강제해서는 안 됩니다. 많은 글로벌 스타일시트가 prefers-reduced-motion에서 애니메이션을 단축합니다. 레코더는 이를 에뮬레이트하지 않으며, 애니메이션을 찾지 못하면 큰 오류를 발생시킵니다.
  • 요소 스크린샷은 "안정성"을 기다리는데, 이는 애니메이션 중간에는 절대 오지 않습니다. 그래서 스크립트가 페이지 영역을 클리핑하는 것입니다.
  • 포트가 실제로 이 앱을 제공하고 있는지 확인하세요 (다른 로컬 서버가 localhost에서 응답할 수 있습니다).

5. 검토

콘택트 시트(contact sheet)를 추출하여 확인하세요:

ffmpeg -y -loglevel error -i og-<page>.mp4 \
  -vf "select='eq(n\,8)+eq(n\,22)+eq(n\,40)+eq(n\,<last>)',scale=1000:-1,tile=2x2" -frames:v 1 sheet.png
  • 프레임 ~0.25초: 빈 카드 (글로우만), 레이아웃 팝 없음.
  • ~0.7초: 제목이 들어오거나 거의 들어옴, 앱 아직 보이지 않음.
  • ~1.3초: 앱이 올라오는 중, 제목 완전히 읽을 수 있음.
  • 마지막 프레임: 정적 PNG와 내용이 동일.

GIF 크기는 적절히 작게 유지하세요 (약 3MB 미만). 더 크다면 --fps를 낮추거나(20–24) --hold를 줄이세요.

출력

사용자에게 다음을 알리세요:

  • 미리보기 라우트 URL (반복 재생) 및 생성된 파일 (MP4, GIF, PNG)
  • 재생성 명령어
  • 타임라인 (무엇이 움직이는지, 지속 시간)
  • og:image는 정적 PNG로 유지해야 함: 소셜 플랫폼은 애니메이션 OG 이미지를 재생하지 않습니다. MP4/GIF는 게시물, README, 런칭 페이지 또는 og:video(지원되는 경우)에 사용하세요.

Individual skills in this repo

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

관련 스킬