HyperFrames은(는) 무엇을 하나요?
처음 사용하시나요? 시작하기 전에 환경 확인을 실행하세요:
python3 /var/minis/skills/hyperframes-cli/scripts/check-env.py이 스크립트는
minis-browser-use,ffmpeg, 그리고 필요한 스킬들이 설치되어 있는지 확인합니다.hyperframes-cli스킬이 없다면 다음 명령어로 설치하세요:python3 /var/minis/skills/hyperframes-cli/scripts/install-skill.py \ hyperframes-cli --repo heygen-com/hyperframes --subdir skills
HTML이 비디오의 진실 공급원(source of truth)입니다. 컴포지션은 data-* 속성으로 타이밍을 지정하고, GSAP 타임라인으로 애니메이션을 구현하며, CSS로 시각적 표현을 정의하는 HTML 파일입니다. 프레임워크가 클립 가시성, 미디어 재생, 타임라인 동기화를 처리합니다.
접근법
HTML을 작성하기 전에 높은 수준에서 생각하세요:
- 무엇 — 시청자가 무엇을 경험해야 하는가? 내러티브 아크, 핵심 순간, 감정적 비트를 파악하세요.
- 구조 — 몇 개의 컴포지션이 필요한가? 하위 컴포지션과 인라인 컴포지션은 무엇인가? 어떤 트랙이 무엇(비디오, 오디오, 오버레이, 캡션)을 담당하는가?
- 타이밍 — 어떤 클립이 지속 시간을 결정하는가? 전환은 어디에 위치하는가? 페이싱은 어떻게 되는가?
- 레이아웃 — 먼저 최종 상태를 구축하세요. 아래 "애니메이션 전 레이아웃"을 참조하세요.
- 애니메이션 — 그런 다음 아래 규칙을 사용하여 모션을 추가하세요.
작은 수정(색상 변경, 타이밍 조정, 요소 하나 추가)의 경우 바로 규칙으로 건너뛰세요.
시각적 정체성 게이트
다음 순서로 확인하세요:
- 프로젝트에 DESIGN.md가 있는가? → 읽으세요. 정확한 색상, 폰트, 모션 규칙, "하지 말아야 할 것" 제약 조건을 사용하세요.
- visual-style.md가 있는가? → 읽으세요.
style_prompt_full과 구조화된 필드를 적용하세요. (참고:visual-style.md는 프로젝트별 파일입니다.visual-styles.md는 8개의 명명된 프리셋이 있는 스타일 라이브러리로, 다른 파일입니다.) - 사용자가 스타일을 지정했는가 (예: "Swiss Pulse", "어둡고 기술적인", "럭셔리 브랜드")? → visual-styles.md에서 8개의 명명된 프리셋을 읽으세요. 다음을 포함한 최소한의 DESIGN.md를 생성하세요:
## Style Prompt(한 문단),## Colors(역할과 함께 3-5개의 16진수 값),## Typography(1-2개의 폰트 패밀리),## What NOT to Do(3-5개의 안티 패턴). - 위 중 해당하는 것이 없는가? → HTML을 작성하기 전에 3가지 질문을 하세요:
- 분위기는 무엇인가? (폭발적/영화적/유동적/기술적/혼란스러운/따뜻한)
- 밝은 캔버스인가 어두운 캔버스인가?
- 특정 브랜드 색상, 폰트, 또는 시각적 참고 자료가 있는가? 그런 다음 답변에서 최소한의 DESIGN.md를 생성하세요.
모든 컴포지션은 팔레트와 타이포그래피를 DESIGN.md, visual-style.md, 또는 명시적인 사용자 지시로 추적할 수 있어야 합니다. #333, #3b82f6, 또는 Roboto를 사용하려 한다면 — 이 단계를 건너뛴 것입니다.
모션 기본값, 크기, 진입 패턴, 이징(easing)에 대해서는 house-style.md를 따르세요. 하우스 스타일은 요소가 어떻게 움직이는지 처리합니다. DESIGN.md는 요소가 어떻게 보이는지 처리합니다.
애니메이션 전 레이아웃
모든 요소를 가장 잘 보이는 순간 — 완전히 진입하고, 올바르게 배치되며, 아직 나가지 않은 프레임 — 에 위치시킬 위치에 배치하세요. 이를 정적 HTML+CSS로 먼저 작성하세요. 아직 GSAP은 사용하지 마세요.
이것이 중요한 이유: 요소를 애니메이션 시작 상태(화면 밖, 0으로 축소, 투명도 0)에 배치하고 도착할 위치로 트윈(tween)하면 최종 레이아웃을 추측하는 것입니다. 겹침은 비디오가 렌더링될 때까지 보이지 않습니다. 최종 상태를 먼저 구축하면 모션을 추가하기 전에 레이아웃 문제를 보고 수정할 수 있습니다.
프로세스
- 각 장면의 히어로 프레임을 식별하세요 — 가장 많은 요소가 동시에 보이는 순간입니다. 이것이 여러분이 구축할 레이아웃입니다.
- 해당 프레임에 대한 정적 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는 장식 요소에만 사용하세요. gsap.from()으로 진입 추가 — 화면 밖/보이지 않음에서 CSS 위치로 애니메이션합니다. CSS 위치가 진실이며, 트윈은 그곳에 도달하는 여정을 설명합니다.gsap.to()로 종료 추가 — CSS 위치에서 화면 밖/보이지 않음으로 애니메이션합니다.
예시
/* 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단계: 해당 위치로 애니메이션 진입
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단계: 해당 위치에서 애니메이션 종료
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가 진입)이 있는 경우, 모든 장면의 요소를 하나의 공유 flex 컨테이너에 절대 넣지 마세요. 각 요소가 opacity: 0으로 시작하더라도 flex는 여전히 모든 하위 요소에 높이를 할당합니다. 하나의 컨테이너에 5개 장면의 콘텐츠가 있으면 컨테이너의 총 높이가 스테이지 높이를 훨씬 초과합니다 — flex justify-content: center는 그 과도하게 큰 컨테이너를 기준으로 콘텐츠를 중앙에 배치하여, 보이는 콘텐츠를 스크린샷 자르기 영역 밖으로 밀어냅니다.
잘못된 예 — 모든 장면이 하나의 flex 컨테이너에:
<div class="scene-content"> <!-- flex column, height:100% -->
<!-- 장면 A 요소 -->
<div id="title-a" style="opacity:0">제목 A</div>
<div id="body-a" style="opacity:0">본문 A</div>
<!-- 장면 B 요소 — 보이지 않아도 flex 높이를 차지함 -->
<div id="title-b" style="opacity:0">제목 B</div>
<div id="body-b" style="opacity:0">본문 B</div>
<!-- 장면 C ... -->
</div>
올바른 예 — 각 장면이 독립적인 절대 레이어:
<!-- 장면 A -->
<div class="scene" id="scene-a">
<div id="title-a" style="opacity:0">제목 A</div>
<div id="body-a" style="opacity:0">본문 A</div>
</div>
<!-- 장면 B — 장면 A에 높이 영향을 주지 않음 -->
<div class="scene" id="scene-b">
<div id="title-b" style="opacity:0">제목 B</div>
<div id="body-b" style="opacity:0">본문 B</div>
</div>
/* 각 장면이 독립적으로 스테이지를 채움 */
.scene {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 80px 120px;
box-sizing: border-box;
z-index: 2;
gap: 24px;
}
이것이 순차적 장면에 대한 유일한 올바른 패턴입니다. 각 장면 컨테이너의 position: absolute; inset: 0은 "애니메이션 전 레이아웃" 가이드라인과 충돌하지 않습니다 — 해당 가이드라인은 하드코딩된 픽셀 오프셋으로 콘텐츠 요소(제목, 카드, 통계)를 절대 위치시키는 것을 금지합니다. inset: 0으로 전체 스테이지를 채우는 장면 컨테이너는 올바르며 필수입니다.
요소가 시간에 걸쳐 공간을 공유할 때
요소 A가 동일한 영역에서 요소 B가 진입하기 전에 종료된다면, 둘 다 각자의 히어로 프레임에 대한 올바른 CSS 위치를 가져야 합니다. 타임라인 순서는 시각적으로 공존하지 않음을 보장합니다 — 그러나 레이아웃 단계를 건너뛰면 타이밍 오류로 인해 실수로 겹치는 경우를 잡을 수 없습니다.
의도적인 겹침으로 간주되는 것
계층 효과(텍스트 뒤의 광선, 그림자 요소, 배경 패턴)와 z-스택 디자인(카드 스택, 깊이 레이어)은 의도적입니다. 레이아웃 단계는 의도하지 않은 겹침 — 두 개의 헤드라인이 서로 위에 겹쳐짐, 통계가 레이블을 가림, 콘텐츠가 프레임 밖으로 벗어남 — 을 잡기 위한 것입니다.
데이터 속성
모든 클립
| 속성 | 필수 여부 | 값 |
|---|---|---|
id | 예 | 고유 식별자 |
data-start | 예 | 초 또는 클립 ID 참조 ("el-1", "intro + 2") |
data-duration | img/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>을 사용하지 않습니다 — <body>에 직접 data-composition-id div를 넣습니다. 독립형 파일에 <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 =