Communitygithub.com

Parcha-ai/build

HyperFrames HTML을 사용하여 비디오 컴포지션, 애니메이션, 타이틀 카드, 오버레이, 캡션, 음성 해설, 오디오 반응형 비주얼 및 장면 전환을 생성합니다.

build란 무엇인가요?

이 스킬은 HyperFrames 프레임워크를 사용하여 HTML 기반 비디오 콘텐츠를 제작하는 방법을 정의합니다. HTML 파일이 비디오의 단일 진실 공급원(source of truth)이 되며, `data-*` 속성으로 타이밍을, GSAP 타임라인으로 애니메이션을, CSS로 스타일을 관리합니다. 이 스킬은 모든 HTML 기반 비디오 콘텐츠를 제작하거나, 오디오와 동기화된 캡션 및 자막을 추가하거나, 텍스트 음성 변환 내레이션을 생성하거나, 오디오 반응형 애니메이션(비트 동기화, 글로우, 음악에 따른 펄스)을 만들거나, 마커 스위프, 손으로 그린 원, 버스트 라인, 낙서, 스케치아웃과 같은 애니메이션 텍스트 하이라이팅을 추가하거나, 장면 간 전환(크로스페이드, 와이프, 리빌, 셰이더 전환)을 추가할 때 사용됩니다. 주요 특징으로는 장면 레이아웃을 애니메이션보다 먼저 구축하는 '레이아웃 우선' 접근법, 모든 장면 전환에 대한 엄격한 규칙(점프 컷 금지, 퇴장 애니메이션 없음 등), `data-start`, `data-duration`, `data-track-index`와 같은 데이터 속성을 통한 구조화된 컴포지션, 그리고 `Math.random()`, 비동기 타임라인 구축, 무한 반복과 같은 비결정적(non-deterministic) 패턴을 금지하는 강력한 가드레일이 포함됩니다. 이 스킬은 HyperFrames 기반 워크플로우에서 작동하며, CLI 명령어(init, lint, preview, render, transcribe, tts)는 별도의 `hyperframes-cli` 스킬에서 다룹니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Parcha-ai/build/tree/HEAD/.agents/skills/hyperframes

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

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

미리보기

스킬 README에서

문서

HyperFrames

HTML이 비디오의 단일 진실 공급원(source of truth)입니다. 컴포지션은 타이밍을 위한 data-* 속성, 애니메이션을 위한 GSAP 타임라인, 그리고 모양을 위한 CSS가 포함된 HTML 파일입니다. 프레임워크는 클립 가시성, 미디어 재생 및 타임라인 동기화를 처리합니다.

접근법

HTML을 작성하기 전에 높은 수준에서 생각하십시오:

  1. 무엇(What) — 시청자는 무엇을 경험해야 합니까? 내러티브 아크, 핵심 순간, 감정적 비트를 식별하세요.
  2. 구조(Structure) — 얼마나 많은 컴포지션이 있고, 어떤 것이 하위 컴포지션이고 인라인인지, 어떤 트랙이 무엇을 전달하는지(비디오, 오디오, 오버레이, 캡션) 확인하세요.
  3. 타이밍(Timing) — 어떤 클립이 지속 시간을 결정하는지, 전환이 어디에 위치하는지, 페이싱은 어떻게 되는지 확인하세요.
  4. 레이아웃(Layout) — 먼저 최종 상태를 구축하세요. 아래의 '레이아웃 우선(Layout Before Animation)'을 참조하세요.
  5. 애니메이션(Animate) — 그런 다음 아래 규칙을 사용하여 모션을 추가하세요.

작은 편집(색상 수정, 타이밍 조정, 요소 하나 추가)의 경우 바로 규칙으로 건너뛰세요.

visual-style.md나 애니메이션 방향이 제공되지 않은 경우, 모션 기본값, 크기 조정 및 색상 팔레트에 대해 house-style.md를 따르세요.

레이아웃 우선(Layout Before Animation)

각 요소를 가장 잘 보이는 순간 — 완전히 들어와서 올바르게 배치되고 아직 나가지 않는 프레임 — 에서 위치시킵니다. 이것을 먼저 정적 HTML+CSS로 작성하세요. 아직 GSAP는 없습니다.

이것이 중요한 이유: 애니메이션 시작 상태(화면 밖, 크기 0으로 축소, 투명도 0)로 요소를 배치하고 생각하는 최종 위치로 트윈하면 최종 레이아웃을 추측하는 것입니다. 중복은 비디오가 렌더링될 때까지 보이지 않습니다. 먼저 최종 상태를 구축하면 모션을 추가하기 전에 레이아웃 문제를 보고 수정할 수 있습니다.

프로세스

  1. 각 장면의 히어로 프레임 — 대부분의 요소가 동시에 보이는 순간 — 을 식별하세요. 이것이 구축할 레이아웃입니다.
  2. 해당 프레임에 대한 정적 CSS를 작성하세요. .scene-content 컨테이너는 width: 100%; height: 100%; padding: Npx;와 display: flex; flex-direction: column; gap: Npx; box-sizing: border-box를 사용하여 전체 장면을 채워야 합니다. 패딩을 사용하여 콘텐츠를 안쪽으로 밀어넣으세요. 콘텐츠 컨테이너에 position: absolute; top: Npx를 절대 사용하지 마세요. 절대 위치 콘텐츠 컨테이너는 콘텐츠가 남은 공간보다 더 클 때 넘칩니다. position: absolute는 장식 요소에만 사용하세요.
  3. gsap.from()으로 등장 애니메이션을 추가하세요 — 화면 밖/보이지 않음에서 CSS 위치로 FROM 애니메이션을 만듭니다. CSS 위치가 기본 사실(ground truth)이고, 트윈은 거기에 도달하는 여정을 설명합니다.
  4. gsap.to()로 퇴장 애니메이션을 추가하세요 — CSS 위치에서 화면 밖/보이지 않음으로 TO 애니메이션을 만듭니다.

예시

/* scene-content가 장면을 채우고, 패딩이 콘텐츠를 위치시킴 */
.scene-content {
  display: flex;
  flex-direction: column;
  justify-content: center;
  width: 100%;
  height: 100%;
  padding: 120px 160px;
  gap: 24px;
  box-sizing: border-box;
}
.title {
  font-size: 120px;
}
.subtitle {
  font-size: 42px;
}
/* 컨테이너는 모든 장면 크기(1920x1080, 1080x1920 등)를 채웁니다.
   패딩이 콘텐츠를 위치시킵니다. Flex + gap이 간격을 처리합니다. */

잘못된 예 — 하드코딩된 크기와 절대 위치:

.scene-content {
  position: absolute;
  top: 200px;
  left: 160px;
  width: 1920px;
  height: 1080px;
  display: flex; /* ... */
}
// 3단계: 해당 위치로 IN 애니메이션
 tl.from(".title", { y: 60, opacity: 0, duration: 0.6, ease: "power3.out" }, 0);
 tl.from(".subtitle", { y: 40, opacity: 0, duration: 0.5, ease: "power3.out" }, 0.2);
 tl.from(".logo", { scale: 0.8, opacity: 0, duration: 0.4, ease: "power2.out" }, 0.3);

// 4단계: 해당 위치에서 OUT 애니메이션
 tl.to(".title", { y: -40, opacity: 0, duration: 0.4, ease: "power2.in" }, 3);
 tl.to(".subtitle", { y: -30, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.1);
 tl.to(".logo", { scale: 0.9, opacity: 0, duration: 0.3, ease: "power2.in" }, 3.2);

요소가 시간에 따라 공간을 공유하는 경우

요소 A가 요소 B가 같은 영역에 들어오기 전에 나가면, 둘 다 각각의 히어로 프레임에 대해 올바른 CSS 위치를 가져야 합니다. 타임라인 순서는 시각적으로 동시에 존재하지 않음을 보장합니다. 그러나 레이아웃 단계를 건너뛰면 타이밍 오류로 인해 실수로 겹치는 경우를 잡을 수 없습니다.

의도적인 중복으로 간주되는 것

겹쳐진 효과(텍스트 뒤의 글로우, 그림자 요소, 배경 패턴) 및 z-스택 디자인(카드 스택, 깊이 레이어)은 의도적입니다. 레이아웃 단계는 의도하지 않은 중복 — 두 제목이 서로 겹치는 경우, 레이블을 덮는 통계, 프레임 밖으로 넘치는 콘텐츠 — 를 잡기 위한 것입니다.

데이터 속성

모든 클립

속성필수값
id예고유 식별자
data-start예초 단위 또는 클립 ID 참조 ("el-1", "intro + 2")
data-durationimg/div/컴포지션에 필수초 단위. 비디오/오디오는 미디어 지속 시간을 기본값으로 함.
data-track-index예정수. 같은 트랙의 클립은 겹칠 수 없음.
data-media-start아니요소스의 트림 오프셋(초 단위)
data-volume아니요0-1 (기본값 1)

data-track-index는 시각적 레이어링에 영향을 주지 않습니다 — CSS z-index를 사용하세요.

컴포지션 클립

속성필수값
data-composition-id예고유 컴포지션 ID
data-start예시작 시간 (루트 컴포지션: "0" 사용)
data-duration예GSAP 타임라인 지속 시간보다 우선함
data-width / data-height예픽셀 크기 (1920x1080 또는 1080x1920)
data-composition-src아니요외부 HTML 파일 경로

컴포지션 구조

data-composition-src를 통해 로드된 하위 컴포지션은 <template> 래퍼를 사용합니다. 독립형 컴포지션(메인 index.html)은 <template>을 사용하지 않습니다 — data-composition-id div를 <body>에 직접 배치합니다. 독립형 파일에 <template>을 사용하면 브라우저에서 모든 콘텐츠를 숨기고 렌더링을 깨뜨립니다.

하위 컴포지션 구조:

<template id="my-comp-template">
 <div data-composition-id="my-comp" data-width="1920" data-height="1080">
   <!-- 콘텐츠 -->
   <style>
     [data-composition-id="my-comp"] {
       /* 스코프 스타일 */
     }
   </style>
   <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
   <script>
     window.__timelines = window.__timelines || {};
     const tl = gsap.timeline({ paused: true });
     // 트윈...
     window.__timelines["my-comp"] = tl;
   </script>
 </div>
</template>

루트에서 로드: <div id="el-1" data-composition-id="my-comp" data-composition-src="compositions/my-comp.html" data-start="0" data-duration="10" data-track-index="1"></div>

비디오 및 오디오

비디오는 muted playsinline이어야 합니다. 오디오는 항상 별도의 <audio> 요소입니다:

<video
 id="el-v"
 data-start="0"
 data-duration="30"
 data-track-index="0"
 src="video.mp4"
 muted
 playsinline
></video>
<audio
 id="el-a"
 data-start="0"
 data-duration="30"
 data-track-index="2"
 src="video.mp4"
 data-volume="1"
></audio>

타임라인 계약

  • 모든 타임라인은 { paused: true }로 시작합니다 — 플레이어가 재생을 제어합니다.
  • 모든 타임라인을 등록하세요: window.__timelines["<composition-id>"] = tl
  • 프레임워크가 하위 타임라인을 자동으로 중첩합니다 — 수동으로 추가하지 마세요.
  • 지속 시간은 GSAP 타임라인 길이가 아닌 data-duration에서 가져옵니다.
  • 지속 시간을 설정하기 위해 빈 트윈을 만들지 마세요.

규칙 (협상 불가)

결정론적(Deterministic): Math.random(), Date.now() 또는 시간 기반 로직을 사용하지 마세요. 유사 난수 값이 필요하면 시드된 PRNG(예: mulberry32)를 사용하세요.

GSAP: 시각적 속성(opacity, x, y, scale, rotation, color, backgroundColor, borderRadius, 변환)만 애니메이션 처리하세요. visibility, display를 애니메이션 처리하거나 video.play()/audio.play()를 호출하지 마세요.

애니메이션 충돌: 여러 타임라인에서 동시에 같은 요소의 같은 속성을 애니메이션 처리하지 마세요.

repeat: -1 금지: 무한 반복 타임라인은 캡처 엔진을 망가뜨립니다. 컴포지션 지속 시간에서 정확한 반복 횟수를 계산하세요: repeat: Math.ceil(duration / cycleDuration) - 1

동기식 타임라인 구축: async/await, setTimeout 또는 Promise 내부에서 타임라인을 절대 구축하지 마세요. 캡처 엔진은 페이지 로드 후 window.__timelines를 동기식으로 읽습니다. 글꼴은 컴파일러에 의해 포함되므로 즉시 사용 가능합니다 — 글꼴 로딩을 기다릴 필요가 없습니다.

절대 하지 말아야 할 것:

  1. window.__timelines 등록을 잊지 마세요.
  2. 오디오에 비디오를 사용하지 마세요 — 항상 음소거된 비디오 + 별도의 <audio>를 사용하세요.
  3. 비디오를 시간이 지정된 div 안에 중첩하지 마세요 — 시간이 지정되지 않은 래퍼를 사용하세요.
  4. data-layer(대신 data-track-index 사용) 또는 data-end(대신 data-duration 사용)를 사용하지 마세요.
  5. 비디오 요소 크기를 애니메이션 처리하지 마세요 — 래퍼 div를 애니메이션 처리하세요.
  6. 미디어에서 play/pause/seek을 호출하지 마세요 — 프레임워크가 재생을 소유합니다.
  7. data-composition-id 없이 최상위 컨테이너를 만들지 마세요.
  8. 어떤 타임라인이나 트윈에도 repeat: -1을 사용하지 마세요 — 항상 유한 반복을 사용하세요.
  9. 타임라인을 비동기식으로 구축하지 마세요 (async, setTimeout, Promise 내부).
  10. 이후 장면의 클립 요소에 gsap.set()을 사용하지 마세요 — 페이지 로드 시 DOM에 존재하지 않습니다. 대신 타임라인 내에서 클립의 data-start 시간 이후에 tl.set(selector, vars, timePosition)을 사용하세요.
  11. 콘텐츠 텍스트에 <br>을 사용하지 마세요 — 강제 줄 바꿈은 실제 렌더링된 글꼴 너비를 고려하지 않습니다. 자연스럽게 줄 바꿈되는 텍스트 + <br>은 원치 않는 추가 줄 바꿈을 발생시켜 겹침을 유발합니다. 대신 max-width를 통해 텍스트를 래핑하세요. 예외: 각 단어가 의도적으로 자체 줄에 있는 짧은 표시 제목(예: 130px에서 "THE\nIMMORTAL\nGAME").

장면 전환 (협상 불가)

모든 다중 장면 컴포지션은 다음 규칙을 모두 따라야 합니다. 하나라도 위반하면 깨진 컴포지션입니다.

  1. 항상 장면 간 전환을 사용하세요. 점프 컷은 금지됩니다. 예외 없음.
  2. 항상 모든 장면에 등장 애니메이션을 사용하세요. 모든 요소는 gsap.from()을 통해 IN 애니메이션됩니다. 어떤 요소도 완전히 형성된 상태로 나타나지 않아야 합니다. 장면에 5개의 요소가 있으면 5개의 등장 트윈이 필요합니다.
  3. 마지막 장면을 제외하고 퇴장 애니메이션을 절대 사용하지 마세요. 이는 다음을 의미합니다: 전환이 실행되기 전에 투명도를 0으로, y를 화면 밖으로, 크기를 0으로 또는 기타 '나가는' 애니메이션을 수행하는 gsap.to()가 없어야 합니다. 전환이 퇴장입니다. 나가는 장면의 콘텐츠는 전환이 시작되는 순간 완전히 보여야 합니다.
  4. 마지막 장면만: 마지막 장면은 요소를 페이드 아웃할 수 있습니다(예: 검은색으로 페이드). 이것이 gsap.to(..., { opacity: 0 })가 허용되는 유일한 장면입니다.

잘못된 예 — 전환 전 퇴장 애니메이션:

// 금지됨 — 전환이 사용하기 전에 장면을 비웁니다.
 tl.to("#s1-title", { opacity: 0, y: -40, duration: 0.4 }, 6.5);
 tl.to("#s1-subtitle", { opacity: 0, duration: 0.3 }, 6.7);
// 전환이 빈 프레임에서 실행됩니다.

올바른 예 — 등장만, 전환이 퇴장 처리:

// 장면 1 등장 애니메이션
 tl.from("#s1-title", { y: 50, opacity: 0, duration: 0.7, ease: "power3.out" }, 0.3);
 tl.from("#s1-subtitle", { y: 30, opacity: 0, duration: 0.5, ease: "power2.out" }, 0.6);
// 퇴장 트윈 없음 — 7.2초의 전환이 장면 변경 처리
// 장면 2 등장 애니메이션
 tl.from("#s2-heading", { x: -40, opacity: 0, duration: 0.6, ease: "expo.out" }, 8.0);

애니메이션 가드레일

  • 첫 번째 애니메이션을 0.1-0.3초 오프셋(t=0이 아님)
  • 등장 트윈 간의 이징(easing)을 다양하게 하세요 — 장면당 최소 3개의 다른 이징을 사용하세요.
  • 장면 내에서 등장 패턴을 반복하지 마세요.
  • 어두운 배경에서 전체 화면 선형 그라데이션을 피하세요(H.264 밴딩 — 방사형 또는 단색 + 국소 글로우 사용).
  • 렌더링된 비디오의 경우 60px+ 제목, 20px+ 본문, 16px+ 데이터 레이블
  • 숫자 열에 font-variant-numeric: tabular-nums

visual-style.md나 애니메이션 방향이 제공되지 않은 경우, 미학적 기본값에 대해 house-style.md를 따르세요.

타이포그래피 및 자산

  • 글꼴: CSS에서 원하는 font-family를 작성하기만 하면 됩니다 — 컴파일러가 지원되는 글꼴을 자동으로 포함합니다. 글꼴이 지원되지 않으면 컴파일러가 경고합니다.
  • 외부 미디어에 crossorigin="anonymous" 추가
  • 동적 텍스트 오버플로우의 경우 window.__hyperframes.fitTextFontSize(text, { maxWidth, fontFamily, fontWeight })를 사용하세요.
  • 모든 파일은 index.html과 함께 프로젝트 루트에 있습니다. 하위 컴포지션은 ../를 사용합니다.

기존 컴포지션 편집

  • 전체 컴포지션을 먼저 읽으세요 — 기존 글꼴, 색상, 애니메이션 패턴과 일치시키세요.
  • 요청된 것만 변경하세요.
  • 관련 없는 클립의 타이밍을 유지하세요.

출력 체크리스트

  • npx hyperframes lint 및 npx hyperframes validate가 모두 통과합니다.
  • 대비 경고가 해결되었습니다(아래 품질 검사 참조).
  • 애니메이션 안무가 확인되었습니다(아래 품질 검사 참조).

품질 검사

대비(Contrast)

hyperframes validate는 기본적으로 WCAG 대비 감사를 실행합니다. 5개의 타임스탬프를 찾아 페이지를 스크린샷으로 찍고, 각 텍스트 요소 뒤의 배경 픽셀을 샘플링하며, 대비율을 계산합니다. 실패는 경고로 나타납니다:

⚠ WCAG AA 대비 경고 (3개):
  · .subtitle "보조 텍스트" — 2.67:1 (4.5:1 필요, t=5.3s)

경고가 나타나면:

  • 어두운 배경에서: 실패하는 색상을 4.5:1(일반 텍스트) 또는 3:1(큰 텍스트, 24px+ 또는 19px+ 굵게)을 충족할 때까지 밝게 합니다.
  • 밝은 배경에서: 어둡게 합니다.
  • 팔레트 계열 내에서 유지하세요 — 새 색상을 발명하지 말고 기존 색상을 조정하세요.
  • 깔끔해질 때까지 hyperframes validate를 다시 실행하세요.

빠르게 반복 중이고 나중에 확인할 경우 --no-contrast를 사용하여 건너뛰세요.

애니메이션 맵

애니메이션을 작성한 후, 안무를 확인하기 위해 애니메이션 맵을 실행하세요:

  • 애니메이션 맵 명령어를 실행하세요(프레임워크 문서 참조).
  • 모든 요소가 예상 순서와 타이밍에 나타나는지 확인하세요.
  • 불필요한 중복이나 간격을 확인하세요.

편집 기존 컴포지션

파일을 불러오기 전에

  • 프로젝트 루트에 있는 모든 .html 파일을 검토하여 전체 구조를 이해하세요.
  • index.html의 루트 컴포지션과 하위 컴포지션의 모든 타이밍 속성을 확인하세요.
  • 사용자 정의 스타일과 애니메이션 패턴을 기록하세요.

편집 원칙

  • 기존의 타이밍, 스타일, 애니메이션 패턴을 유지하세요.
  • 요청된 변경만 수행하세요.
  • 다른 요소와의 충돌을 피하기 위해 변경 사항을 격리하세요.

파일 저장 규칙

  • 변경 사항을 저장할 때 전체 파일 내용을 출력하세요. 부분 업데이트나 diff가 아닙니다.
  • 원본 구조와 주석을 유지하세요.
  • 컴포지션 간의 일관성을 보장하기 위해 전체 파일을 다시 확인하세요.

일반적인 문제 해결

타임라인 문제

  • 타임라인이 등록되었는지 확인: window.__timelines["my-comp"] = tl
  • 지속 시간이 올바르게 설정되었는지 확인: GSAP 타임라인 길이가 아닌 data-duration
  • 하위 타임라인이 중첩되지 않았는지 확인: 프레임워크가 이를 자동 처리합니다.

렌더링 문제

  • 모든 요소에 올바른 data-composition-id가 있는지 확인
  • 미디어 파일 경로가 올바른지 확인
  • 오디오 요소가 비디오와 별도로 설정되었는지 확인
  • 대비 경고 해결 hyperframes validate 실행

애니메이션 문제

  • 동시에 같은 속성을 애니메이션하는 충돌하는 트윈이 없는지 확인
  • 전환이 올바르게 설정되었는지 확인(점프 컷 금지)
  • 등장 애니메이션이 모든 장면에 존재하는지 확인

모범 사례

  • 복잡성을 관리하기 위해 컴포지션을 논리적 크기로 유지하세요.
  • 재사용 가능한 패턴에 대해 의미 있는 ID와 클래스 이름을 사용하세요.
  • 타이밍 및 동작을 문서화하기 위해 주석을 추가하세요.
  • 변경 후 도구를 실행하여 품질을 보장하세요.

예시: 전체 컴포지션

다음은 기본 컴포지션 구조의 예입니다:

<!DOCTYPE html>
<html>
<head>
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
</head>
<body>
  <div data-composition-id="main-comp" data-width="1920" data-height="1080">
    <div class="scene-content">
      <h1 class="title">Hello World</h1>
      <p class="subtitle">A HyperFrames Composition</p>
    </div>
    <style>
      [data-composition-id="main-comp"] .scene-content {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        width: 100%;
        height: 100%;
        padding: 60px;
        gap: 20px;
        box-sizing: border-box;
      }
      .title {
        font-size: 80px;
        color: #ffffff;
      }
      .subtitle {
        font-size: 32px;
        color: #cccccc;
      }
    </style>
    <script>
      window.__timelines = window.__timelines || {};
      const tl = gsap.timeline({ paused: true });

      // 등장 애니메이션
      tl.from(".title", { y: 50, opacity: 0, duration: 0.8, ease: "power3.out" }, 0.2);
      tl.from(".subtitle", { y: 30, opacity: 0, duration: 0.6, ease: "power2.out" }, 0.5);

      window.__timelines["main-comp"] = tl;
    </script>
  </div>
</body>
</html>

이 예시는 핵심 개념을 보여줍니다: 컴포지션은 장면 콘텐츠를 위한 .scene-content 컨테이너와 등장 애니메이션을 위한 GSAP 타임라인을 사용하여 구조화됩니다.

관련 스킬