Communitygithub.com

d0-hyperframe

用 HyperFrames 制作 D0 品牌视频。包含素材提取、项目搭建、场景布局、GSAP 动画、TG 组件规范、转场规则。当用户说

d0-hyperframe 是什么?

d0-hyperframe is a Codex agent skill that 用 HyperFrames 制作 D0 品牌视频。包含素材提取、项目搭建、场景布局、GSAP 动画、TG 组件规范、转场规则。当用户说.

兼容平台~Claude Code✓Codex CLI~Cursor✓Antigravity
npx skills add https://github.com/scof-real/d0-hyperframe-skill/tree/main/skills/d0-hyperframe

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

D0 HyperFrame Video Production

Full workflow for creating D0 brand videos with HyperFrames HTML + GSAP. Use this document as the single authoritative source — fonts, colors, layouts, animations, and transition rules are all defined here.

Font specifications in the d0-brand skill are the final authority; this skill only overrides video-production-specific supplementary rules.


Trigger Phrases

  • "Make a D0 video" / "d0 hyperframe"
  • "Use hyperframes to make a video" / "/d0-hyperframe"

Step 0 — Initialize Project & Assets

Recommended: Use the initialization script provided with the skill

After installing the repository, run the script inside this skill. It will initialize a HyperFrames project and copy the D0 Logo, D0 Avatar, and all required fonts:

bash "${CODEX_HOME:-$HOME/.codex}/skills/d0-hyperframe/scripts/bootstrap_project.sh" .

If the project is already initialized, only sync brand assets:

bash "${CODEX_HOME:-$HOME/.codex}/skills/d0-hyperframe/scripts/bootstrap_project.sh" . --assets-only

The script requires Node.js 18+ and npx. The HyperFrames CLI runs on demand via npx hyperframes@latest; no third-party packages are copied into the skill repository.

Optional: Override D0_AVATAR and Logo from source HTML

Extract D0_AVATAR and Logo from source HTML

The source poster HTML typically embeds two types of base64 images:

AssetTypeCharacteristicsUsage
D0 LogoPNG, ~12-16 KB.brand img or shortest PNGTop left of every scene
D0_AVATARJPEG, ~100 KB.av or longest JPEGTG component avatar
python3 - << 'EOF'
import re, base64, os

with open('SOURCE.html', 'r') as f:
    src = f.read()

os.makedirs('assets', exist_ok=True)

# Logo (shortest PNG)
pngs = re.findall(r'(data:image/png;base64,[A-Za-z0-9+/=]+)', src)
logo_b64 = min(pngs, key=len)
with open('assets/d0-logo.png', 'wb') as f:
    f.write(base64.b64decode(logo_b64.split(',')[1]))

# D0_AVATAR (longest JPEG)
jpegs = re.findall(r'data:image/jpeg;base64,([A-Za-z0-9+/=]+)', src)
avatar = max(jpegs, key=len)
with open('assets/d0-avatar.jpg', 'wb') as f:
    f.write(base64.b64decode(avatar))

print("assets/ ready:", os.listdir('assets'))
EOF

Fonts (manual method)

mkdir -p fonts
cp /path/to/d0-hyperframe/assets/fonts/*.woff2 fonts/
# Required: InstrumentSerif-Regular.woff2, InstrumentSerif-Italic.woff2
#           OpenSans-400/600/700/800-normal.woff2

Project initialization (manual method)

npx hyperframes@latest init . --non-interactive --example blank --skill=d0-hyperframe

Step 1 — Brand Tokens (CSS)

:root {
  --bg:    #06060d;
  --pu2:   #b3a8f7;
  --white: #f2f2f5;
  --t2:    #8d8d98;
  --t3:    #5a5a66;
  --green: #3ddc84;
  --red:   #f87171;
  --gold:  #f7a93a;
  --bdr:   #23232c;
  --bdr2:  #2e2e39;
  --grad:  linear-gradient(120deg, #5468F5 0%, #9B4FD6 34%, #C25C9A 62%, #DD8B57 100%);
}

@font-face { font-family:'Instrument Serif'; src:url('fonts/InstrumentSerif-Regular.woff2') format('woff2'); font-weight:400; font-style:normal; }
@font-face { font-family:'Instrument Serif'; src:url('fonts/InstrumentSerif-Italic.woff2')  format('woff2'); font-weight:400; font-style:italic; }
@font-face { font-family:'Open Sans'; src:url('fonts/OpenSans-400-normal.woff2') format('woff2'); font-weight:400; }
@font-face { font-family:'Open Sans'; src:url('fonts/OpenSans-600-normal.woff2') format('woff2'); font-weight:600; }
@font-face { font-family:'Open Sans'; src:url('fonts/OpenSans-700-normal.woff2') format('woff2'); font-weight:700; }
@font-face { font-family:'Open Sans'; src:url('fonts/OpenSans-800-normal.woff2') format('woff2'); font-weight:800; }

Step 2 — Stage Structure (Persistent Stage + Component Layer)

Core model: one fixed background stage, content split into components. The background + fixed elements (logo/footer/top rail) belong to the persistent layer, remaining throughout; titles, data, TG panels, charts, etc. are each independent .blk components, each animating in and out independently. Transitions happen at the component level, not the full-screen level — this is the fundamental difference from a "PPT hard cut."

HTML Skeleton

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>D0 · [Video Title]</title>
<style>/* tokens + scene CSS */</style>
</head>
<body>
<div id="main"
  data-composition-id="main"
  data-start="0"
  data-duration="[total seconds]"
  data-width="1920"
  data-height="1080">

  <!-- ── Persistent Layer: stays throughout, no tl animations (see Step 3) ── -->
  <div class="bg"></div>
  <div class="rail"></div>
  <div class="logo"></div>
  <div class="foot-tag">Agentic Trading for Every Market.</div>
  <div class="foot-url">getdonut.ai</div>

  <!-- ── Content Layer: grouped by beat, components interleave in/out (see Step 6) ── -->
  <!-- beat 1 -->
  <div class="blk" id="b1-title">...</div>
  <div class="blk" id="b1-sub">...</div>
  <!-- beat 2 -->
  <div class="blk" id="b2-stat">...</div>
  <div class="blk" id="b2-tg">...</div>

</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<script>/* GSAP timeline */</script>
</body>
</html>

Stage CSS (Persistent Layer + Content Layer)

/* Stage container */
#main {
  position: absolute; inset: 0; overflow: hidden;
  font-family: 'Open Sans', sans-serif; color: var(--white);
}

/* Persistent background — fixed throughout, never enters tl animation */
.bg {
  position: absolute; inset: 0; width: 1920px; height: 1080px; z-index: 0;
  background: var(--bg);
  background-image:
    radial-gradient(ellipse 46% 58% at 76% 50%, rgba(150,128,255,.16) 0%, transparent 60%),
    radial-gradient(ellipse 62% 50% at 8%   4%, rgba(110,100,255,.10) 0%, transparent 56%);
}

/* Top gradient rail — fixed, only one needed for the entire video */
.rail {
  position: absolute; top: 0; left: 0; right: 0; height: 5px; z-index: 10;
  background: var(--grad);
}

/* Content components — the smallest unit of animation, hidden by default, each in/out independently */
.blk { position: absolute; opacity: 0; z-index: 2; }

The old model had each full-screen .scene with its own background and sliding block → PPT feel + abrupt stops. The new model uses only one fixed background, content split into .blk components, transitions at the component level (see Step 6).


Step 3 — Fixed Element Specifications (Non-negotiable)

The following three elements are consistent across all scenes and must not have entrance or exit animations:

ElementPositionFont SizeNotes
Donut LogoTop left top:40px left:72px200×76pxCSS background, no <img>
Agentic Trading for Every Market.Bottom left bottom:48px left:72px27pxInstrument Serif italic, --pu2
getdonut.aiBottom right bottom:50px right:72px14pxOpen Sans, --t3
/* Logo — CSS background, do not set opacity:0 */
.logo {
  position: absolute; top: 40px; left: 72px; z-index: 10;
  width: 200px; height: 76px;
  background: url('assets/d0-logo.png') left center / contain no-repeat;
}

/* Footer — directly visible, no animation */
.foot-tag {
  position: absolute; bottom: 48px; left: 72px; z-index: 10;
  font-family: 'Instrument Serif', serif; font-style: italic;
  font-size: 27px; color: var(--pu2);
}

.foot-url {
  position: absolute; bottom: 50px; right: 72px; z-index: 10;
  font-size: 14px; font-weight: 700; letter-spacing: .06em; color: var(--t3); opacity: 0;
}

Fixed elements belong to the persistent layer, visible from frame 0, and no tl animations should be applied to them. foot-tag is directly visible; foot-url may have a one-time subtle fade-in, but it is not mandatory.


Step 4 — Layout Principles

Left-Right Balance, Eliminate Center Empty Space

  • Dual-column layout preferred: copy/data on the left, TG component/image on the right
  • When a character background image is on the right: absolute-position to fill the right side, overlay with a left-to-right gradient mask
.scene-char {
  position: absolute; top:0; right:0; bottom:0; width:62%;
  background: url('assets/bg-char.png') right center / cover no-repeat;
  z-index: 0;
}
.scene-char::after {
  content:''; position:absolute; inset:0;
  background: linear-gradient(to right,
    var(--bg) 0%, rgba(6,6,13,.82) 18%, rgba(6,6,13,.45) 45%, transparent 100%);
}
  • Left copy area should have position: relative; z-index: 2 to ensure it sits above the background image

Minimum Font Size Standards

Based on the D0 Thorp Engine video, must not be smaller than:

LevelMinimum Font SizeFont
Main title80pxInstrument Serif
Scene subtitle56pxInstrument Serif
Kick label / eyebrow18pxOpen Sans 700 uppercase
Body / feature bullet22pxOpen Sans
Data anchor (stat-val)48pxInstrument Serif
Footer tag27pxInstrument Serif italic
TG component text13px (acceptable minimum)Open Sans

Step 5 — TG Component Specifications

Avatar: Must use D0_AVATAR

/* Large avatar (header, 40px) */
.av  { width:40px; height:40px; border-radius:50%; flex:none;
       background: url('assets/d0-avatar.jpg') center/cover no-repeat; }

/* Small avatar (message row, 28px) */
.mav { width:28px; height:28px; border-radius:50%; flex:none; margin-top:2px;
       background: url('assets/d0-avatar.jpg') center/cover no-repeat; }

Do not use <img> tags, to avoid HyperFrames detecting duplicate media nodes (lint warning). Do not use gradient fallback; must be the actual D0_AVATAR file.

TG Panel Structure

<div id="tg-panel">
  <div class="tg-chrome">
    <div class="dot r"></div><div class="dot y"></div><div class="dot g"></div>
    <div class="tg-url">t.me / D0_Bot</div>
  </div>
  <div class="tg-hdr">
    <div class="av"></div>
    <div>
      <div class="tg-nm">D0</div>
      <div class="tg-st">bot · online</div>
    </div>
  </div>
  <div class="tg-body">
    <div class="msg-user">User prompt</div>
    <div class="msg-bot">
      <div class="mav"></div>
      <div class="bbubble">D0 reply content</div>
    </div>
  </div>
</div>

Step 6 — Transition Rules (Non-negotiable)

Core principle: background stays still, components move. The "PPT feel" comes from the entire scene (including background) sliding away together — the audience loses a stable anchor point; The "stutter" comes from out-animations and in-animations being chained end-to-end — creating a moment of complete stillness in between. The solution to both is the same phrase: fixed background + component interleaving, overlapping in/out.

Default Transition: Component-Level Cross-Stagger (element cross-stagger)

The background + fixed elements (logo/footer/rail) stay fixed throughout. Each beat switch only moves the content components:

  • Old beat components exit one by one (stagger, .in ease, subtle offset + fade out)
  • New beat components enter one by one (stagger, .out ease)
  • They overlap by ≈ 0.2–0.3s in time — old components haven't fully exited before the new ones start entering. This is the key to eliminating stutter.
// Beat switch: old components stagger out, new components stagger in, time overlapping (not end-to-end)
function beat(outEls, inEls, t) {
  outEls.forEach((el, i) =>
    tl.to(el, { opacity:0, y:-30, duration:0.45, ease:'power2.in' }, t + i*0.06));
  inEls.forEach((el, i) =>
    tl.fromTo(el, { opacity:0, y:40 },
      { opacity:1, y:0, duration:0.6, ease:'expo.out' }, t + 0.2 + i*0.08));  // +0.2 = overlap
}
// Usage: beat(['#b1-title','#b1-sub'], ['#b2-stat','#b2-tg'], 4.0);
  • Offset amounts should be small (20–40px); direction can be uniform (e.g., upward for a "page-turn airflow"), but the background never participates.
  • When many components are involved, stagger keeps the frame continuously active, avoiding "all enter at once → freeze together."

When to Use Full-Screen Transitions

Only occasionally for major segments (e.g., brand title card → data section), and even then only push the content layer, not the background — wrap that segment's components in a #grp container and push it:

// Push-slide (push only the content container, .bg background stays still)
tl.to('#grpA',     { x:-1920, duration:0.55, ease:'power3.inOut' }, t);
tl.fromTo('#grpB', { x:1920, opacity:1 }, { x:0, duration:0.55, ease:'power3.inOut' }, t);

// Blur crossfade (emotional transition, usually for the final transition)
tl.to('#grpC', { filter:'blur(20px)', opacity:0, duration:0.65, ease:'power2.in' }, t);
tl.fromTo('#grpD', { filter:'blur(20px)', opacity:0 },
  { filter:'blur(0px)', opacity:1, duration:0.65, ease:'power2.out' }, t);

❌ Forbidden: sliding entire scene (including background), corner scaling, full-screen zoom, out/in end-to-end with no overlap.


Step 7 — GSAP Animation Rules

Must Follow

const tl = gsap.timeline({ paused: true });
// ...tweens...
window.__timelines['main'] = tl;
  • Use tl.fromTo() instead of tl.from() (ensures HyperFrames seek determinism)
  • For repeating animations, use Math.floor(duration / cycle) - 1, avoid repeat: -1
  • For multiple animations on the same element, add overwrite: 'auto'
  • Use .out easing for entrances, .in easing for exits

Layout First, Animation Second

First write the element's final state (CSS positioning), then use fromTo to describe the entrance path:

// ✅ Correct
tl.fromTo('#title', { opacity:0, y:40 }, { opacity:1, y:0, duration:0.8, ease:'expo.out' }, 0.3);

No Animations on Persistent Layer

.bg, .rail, .logo, .foot-tag, .foot-url stay fixed throughout; animations only occur on .blk components. Once the background participates in movement, it immediately returns to a PPT feel — transitions only move the content layer (see Step 6 beat()).


Step 8 — Lint & Preview

npx hyperframes lint     # Must have 0 errors
npx hyperframes preview  # http://localhost:300x, hot reload
Common WarningHandling
overlapping_gsap_tweensAdd overwrite:'auto'
duplicate_media_discovery_riskReplace <img> with CSS background:url()
composition_file_too_largeIgnore if single file <500 lines

Dependencies & Asset Sources

  • d0-brand Skill: the final authority on fonts, sizes, and brand principles; must be installed together with this skill.
  • HyperFrames CLI: obtained on demand via npx hyperframes@latest.
  • GSAP: example uses a fixed CDN version 3.14.2; if upgrading, re-run lint and seek validation first.
  • assets/fonts/: Instrument Serif and Open Sans; license files are located in the corresponding font directories.
  • assets/brand/: D0 Logo and D0 Avatar, only for authorized Donut team members to use in D0 brand content.

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

相关技能