mirror of
https://github.com/hanzoai/frames.git
synced 2026-09-24 00:08:03 +00:00
Every SKILL.md already declared the new name — name: frames, frames-cli, frames-core, and so on — but the directories were never moved, so the tree carried frames-* in content and hyperframes-* on disk. Everything that resolves a skill by path missed: the core-skill list, the plugin marketplace pin, and the docs the CLI reads at runtime. Eight tests failed on main because the files they open do not exist. The directories now match what the files say they are, and the guide the claude-design contract reads takes the name that contract already used.
469 lines
15 KiB
HTML
469 lines
15 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=1920, height=1080" />
|
||
<title>Scene 10 — Frames Morph Press Interact</title>
|
||
|
||
<!--
|
||
Frames composition.tsx.
|
||
|
||
Choreography (4 phases, 5.5 seconds total):
|
||
0.17 – 0.64s Hero "Frames" + logo asset fade up (opacity + y rise)
|
||
0.17 – 5.50s Logo asset rotates ±4° continuously (sine onUpdate)
|
||
2.17 – 2.62s Hero shrinks (1 → 0.6) and fades (1 → 0)
|
||
2.17 – 2.62s "Build video from HTML" CTA pill pops in with back.out(2) overshoot
|
||
2.33 – 2.66s CTA text fades + lifts into place (after container is recognizable)
|
||
2.83s Cursor hard-cuts in at off-screen bottom-right
|
||
2.83 – 3.83s Cursor approaches via spring path to (W/2 + 120, H/2 + 50)
|
||
3.83 – 3.98s Click DOWN: both cursor and CTA compress to scale 0.9 (sync)
|
||
4.17 – 4.42s Click UP: both return to scale 1.0 (sync)
|
||
DOM + GSAP opacity tweens; z-index keeps incoming above outgoing
|
||
- Press synchronization uses single GSAP target array ["#cta", "#cursor"]
|
||
to guarantee perfectly identical tween values
|
||
- Cursor opacity uses a 0.001-second fromTo for hard-cut step change
|
||
- Logo uses a local static image asset referenced with a plain URL
|
||
- Breathing rotation only on the logo, not on the whole hero (avoids
|
||
conflict with the morph exit scale tween)
|
||
-->
|
||
|
||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
|
||
|
||
<style>
|
||
:root {
|
||
--hf-bg: #050711;
|
||
--hf-bg-2: #0b1020;
|
||
--hf-ink: #f8fafc;
|
||
--hf-muted: #cbd5e1;
|
||
--hf-purple: #7c3aed;
|
||
--hf-purple-soft: #a78bfa;
|
||
--hf-cyan: #18d9e8;
|
||
--hf-green: #22c55e;
|
||
--hf-glass: rgba(248, 250, 252, 0.08);
|
||
--white: #ffffff;
|
||
}
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
html,
|
||
body {
|
||
margin: 0;
|
||
width: 1920px;
|
||
height: 1080px;
|
||
overflow: hidden;
|
||
background:
|
||
radial-gradient(ellipse at 50% 42%, rgba(24, 217, 232, 0.14), transparent 50%),
|
||
radial-gradient(ellipse at 62% 48%, rgba(124, 58, 237, 0.2), transparent 56%),
|
||
radial-gradient(ellipse at 42% 58%, rgba(34, 197, 94, 0.1), transparent 58%),
|
||
linear-gradient(135deg, #050711 0%, #090b18 48%, #120717 100%);
|
||
font-family:
|
||
"Inter",
|
||
system-ui,
|
||
-apple-system,
|
||
sans-serif;
|
||
color: var(--hf-ink);
|
||
}
|
||
|
||
.stage {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* ============================================================
|
||
HERO LOCKUP (Phase 1 — exits during morph)
|
||
============================================================ */
|
||
.hero {
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 54px;
|
||
will-change: transform, opacity;
|
||
}
|
||
.hero-text {
|
||
font-size: 220px;
|
||
font-weight: 800;
|
||
color: var(--hf-ink);
|
||
letter-spacing: 0;
|
||
line-height: 1;
|
||
margin: 0;
|
||
text-shadow:
|
||
0 0 34px rgba(167, 139, 250, 0.18),
|
||
0 0 72px rgba(24, 217, 232, 0.1);
|
||
}
|
||
.hero-text .brand-accent {
|
||
color: var(--hf-purple-soft);
|
||
text-shadow:
|
||
0 0 34px rgba(124, 58, 237, 0.45),
|
||
0 0 82px rgba(167, 139, 250, 0.22);
|
||
}
|
||
.hero-logo {
|
||
width: 300px;
|
||
height: 300px;
|
||
margin-left: 8px;
|
||
will-change: transform;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.hero-logo .logo-mark {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: block;
|
||
border-radius: 36px;
|
||
box-shadow:
|
||
0 0 64px rgba(24, 217, 232, 0.32),
|
||
0 0 112px rgba(34, 197, 94, 0.18),
|
||
0 0 150px rgba(124, 58, 237, 0.12);
|
||
}
|
||
|
||
/* ============================================================
|
||
CTA PILL (Phase 2 — pops in during morph)
|
||
============================================================ */
|
||
.cta {
|
||
background: linear-gradient(
|
||
135deg,
|
||
rgba(124, 58, 237, 0.96) 0%,
|
||
rgba(24, 217, 232, 0.9) 48%,
|
||
rgba(34, 197, 94, 0.96) 100%
|
||
);
|
||
padding: 70px 200px;
|
||
border-radius: 200px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
overflow: hidden;
|
||
border: 1px solid rgba(24, 217, 232, 0.55);
|
||
box-shadow:
|
||
0 24px 60px rgba(0, 0, 0, 0.42),
|
||
0 0 72px rgba(24, 217, 232, 0.24),
|
||
0 0 120px rgba(124, 58, 237, 0.24),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.24),
|
||
inset 0 0 34px var(--hf-glass);
|
||
will-change: transform, opacity;
|
||
/* initial scale 0 + opacity 0 set by gsap.set() */
|
||
}
|
||
.cta-text {
|
||
color: var(--hf-bg);
|
||
font-size: 110px;
|
||
font-weight: 700;
|
||
white-space: nowrap;
|
||
text-shadow:
|
||
0 1px 0 rgba(255, 255, 255, 0.3),
|
||
0 0 22px rgba(255, 255, 255, 0.12);
|
||
will-change: transform, opacity;
|
||
/* initial opacity 0 + y 10 set by gsap.set() */
|
||
}
|
||
|
||
/* ============================================================
|
||
CURSOR (Phase 3 — hard-cuts in)
|
||
============================================================ */
|
||
.cursor {
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
width: 120px;
|
||
height: 120px;
|
||
will-change: transform, opacity;
|
||
/* initial x/y/opacity set by gsap.set() */
|
||
}
|
||
.cursor .cursor-svg {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: block;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div
|
||
id="root"
|
||
data-composition-id="main"
|
||
data-start="0"
|
||
data-duration="5.5"
|
||
data-width="1920"
|
||
data-height="1080"
|
||
>
|
||
<div class="stage clip" data-start="0" data-duration="5.5" data-track-index="1" id="stage">
|
||
<!-- =====================================================
|
||
HERO LOCKUP — "Frames" + logo
|
||
===================================================== -->
|
||
<div
|
||
class="hero clip"
|
||
id="hero"
|
||
data-start="0"
|
||
data-duration="2.62"
|
||
data-track-index="2"
|
||
data-layout-allow-overflow
|
||
aria-hidden="true"
|
||
>
|
||
<h1 class="hero-text">Hyper<span class="brand-accent">Frames</span></h1>
|
||
<div class="hero-logo" id="hero-logo">
|
||
<!-- Inline-SVG placeholder mark — swap for your logo image -->
|
||
<svg class="logo-mark" viewBox="0 0 100 100" role="img" aria-label="Frames">
|
||
<defs>
|
||
<linearGradient id="hfMark" x1="0" y1="0" x2="1" y2="1">
|
||
<stop offset="0" stop-color="#8b7bff" />
|
||
<stop offset="1" stop-color="#3ddc97" />
|
||
</linearGradient>
|
||
</defs>
|
||
<rect x="4" y="4" width="92" height="92" rx="22" fill="url(#hfMark)" />
|
||
<text
|
||
x="50"
|
||
y="63"
|
||
text-anchor="middle"
|
||
font-family="Inter, system-ui, sans-serif"
|
||
font-size="40"
|
||
font-weight="800"
|
||
fill="#fff"
|
||
>
|
||
HF
|
||
</text>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- =====================================================
|
||
CTA PILL — "Build video from HTML"
|
||
===================================================== -->
|
||
<div class="cta" id="cta">
|
||
<span class="cta-text">Build video from HTML</span>
|
||
</div>
|
||
|
||
<!-- =====================================================
|
||
CURSOR — inline SVG arrow with drop-shadow
|
||
===================================================== -->
|
||
<div class="cursor" id="cursor">
|
||
<svg
|
||
class="cursor-svg"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
aria-hidden="true"
|
||
>
|
||
<defs>
|
||
<filter id="cursorShadow" x="-50%" y="-50%" width="200%" height="200%">
|
||
<feDropShadow dx="2" dy="4" stdDeviation="3" flood-opacity="0.3" />
|
||
</filter>
|
||
</defs>
|
||
<path
|
||
d="M5.5 3.5L19 10.5L11.5 12.5L15.5 19.5L13.5 20.5L9.5 13.5L5.5 17.5V3.5Z"
|
||
fill="#050711"
|
||
stroke="#f8fafc"
|
||
stroke-width="1.5"
|
||
stroke-linejoin="round"
|
||
filter="url(#cursorShadow)"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
/* ================================================================
|
||
TIMING
|
||
================================================================ */
|
||
const W = 1920,
|
||
H = 1080;
|
||
const TOTAL_DUR = 5.5;
|
||
|
||
const TIMING = {
|
||
introStart: 0.17, // ~5 frames at 30 fps
|
||
introDur: 0.47, // hero enter spring settle
|
||
|
||
morphAt: 2.17, // ~65 frames
|
||
morphExitDur: 0.5,
|
||
morphFadeDur: 0.15, // 30% of exit
|
||
morphEntDur: 0.45, // CTA pop-in
|
||
|
||
textRevealAt: 2.33, // morph + 0.17 s
|
||
textRevealDur: 0.33,
|
||
|
||
cursorEnterAt: 2.83,
|
||
cursorPathDur: 1.0,
|
||
|
||
clickDownAt: 3.83,
|
||
clickDownDur: 0.15,
|
||
clickUpAt: 4.17,
|
||
clickUpDur: 0.25,
|
||
|
||
pressIntensity: 0.1,
|
||
};
|
||
|
||
/* ================================================================
|
||
INITIAL STATES (via gsap.set, before the timeline runs)
|
||
================================================================ */
|
||
// Hero: starts at opacity 0, y +40 (will fade up)
|
||
gsap.set("#hero", { opacity: 0, y: 40 });
|
||
|
||
// CTA: starts invisible (scale 0, opacity 0) — morph pops it in
|
||
gsap.set("#cta", { scale: 0, opacity: 0 });
|
||
|
||
// CTA text: starts at opacity 0 + y 10 — reveals after container is recognizable
|
||
gsap.set(".cta-text", { opacity: 0, y: 10 });
|
||
|
||
// Cursor: starts off-screen bottom-right + invisible + scale 1 baseline
|
||
gsap.set("#cursor", {
|
||
x: W + 100,
|
||
y: H + 200,
|
||
scale: 1,
|
||
opacity: 0,
|
||
});
|
||
|
||
/* ================================================================
|
||
TIMELINE
|
||
================================================================ */
|
||
window.__timelines = window.__timelines || {};
|
||
const tl = gsap.timeline({ paused: true });
|
||
window.__timelines["main"] = tl;
|
||
|
||
/* ----------------------------------------------------------------
|
||
PHASE 1: Hero entrance + breathing rotation on logo
|
||
---------------------------------------------------------------- */
|
||
tl.to(
|
||
"#hero",
|
||
{
|
||
opacity: 1,
|
||
y: 0,
|
||
duration: TIMING.introDur,
|
||
ease: "power3.out", // spring(stiffness:120, damping:14)
|
||
},
|
||
TIMING.introStart,
|
||
);
|
||
|
||
// Continuous breathing rotation on the logo. ±4° over ~6.3 s period.
|
||
// For a 5.5 s composition this is just under one cycle — subtle but alive.
|
||
// Use Form 2 (onUpdate reading tl.time) per sine-wave-loop rule.
|
||
const logoEl = document.querySelector("#hero-logo");
|
||
tl.to(
|
||
{ tick: 0 },
|
||
{
|
||
tick: 1,
|
||
duration: TOTAL_DUR,
|
||
ease: "none",
|
||
onUpdate: function () {
|
||
const t = tl.time();
|
||
gsap.set(logoEl, { rotation: Math.sin(t * 1.0) * 4 });
|
||
},
|
||
},
|
||
0,
|
||
);
|
||
|
||
/* ----------------------------------------------------------------
|
||
PHASE 2: Scale-swap morph
|
||
Three concurrent tween clusters from MORPH_AT.
|
||
---------------------------------------------------------------- */
|
||
// (1) Hero shrinks
|
||
tl.to(
|
||
"#hero",
|
||
{
|
||
scale: 0.6,
|
||
duration: TIMING.morphExitDur,
|
||
ease: "power3.out", // spring(stiffness:150, damping:18)
|
||
},
|
||
TIMING.morphAt,
|
||
);
|
||
|
||
// (2) Hero fades fast (30% of shrink dur)
|
||
tl.to(
|
||
"#hero",
|
||
{
|
||
opacity: 0,
|
||
duration: TIMING.morphFadeDur,
|
||
ease: "power2.out",
|
||
},
|
||
TIMING.morphAt,
|
||
);
|
||
|
||
tl.set(
|
||
"#hero",
|
||
{
|
||
x: -4000,
|
||
},
|
||
TIMING.morphAt + TIMING.morphFadeDur,
|
||
);
|
||
|
||
// (3) CTA pops in with overshoot
|
||
tl.to(
|
||
"#cta",
|
||
{
|
||
scale: 1,
|
||
opacity: 1,
|
||
duration: TIMING.morphEntDur,
|
||
ease: "back.out(2)", // spring(stiffness:200, damping:15, mass:0.6)
|
||
},
|
||
TIMING.morphAt,
|
||
);
|
||
|
||
// (4) CTA text reveals after container reaches recognizable scale
|
||
tl.to(
|
||
".cta-text",
|
||
{
|
||
opacity: 1,
|
||
y: 0,
|
||
duration: TIMING.textRevealDur,
|
||
ease: "power2.out",
|
||
},
|
||
TIMING.textRevealAt,
|
||
);
|
||
|
||
/* ----------------------------------------------------------------
|
||
PHASE 3: Cursor entry + motion path
|
||
---------------------------------------------------------------- */
|
||
// Hard-cut opacity (cursors don't fade in — they appear).
|
||
// 0.001 s tween creates a step change that scrubs correctly.
|
||
tl.fromTo(
|
||
"#cursor",
|
||
{ opacity: 0 },
|
||
{ opacity: 1, duration: 0.001, ease: "none" },
|
||
TIMING.cursorEnterAt,
|
||
);
|
||
|
||
// Spring-driven approach to a target slightly offset from center
|
||
// (where a human would naturally aim — not dead-center).
|
||
tl.to(
|
||
"#cursor",
|
||
{
|
||
x: W / 2 + 120,
|
||
y: H / 2 + 50,
|
||
duration: TIMING.cursorPathDur,
|
||
ease: "power2.out", // spring(stiffness:60, damping:20)
|
||
},
|
||
TIMING.cursorEnterAt,
|
||
);
|
||
|
||
/* ----------------------------------------------------------------
|
||
PHASE 4: Physics-based press
|
||
Both CTA and cursor compress together — single target array.
|
||
---------------------------------------------------------------- */
|
||
// Press DOWN — both elements compress to (1 - intensity)
|
||
tl.to(
|
||
["#cta", "#cursor"],
|
||
{
|
||
scale: 1 - TIMING.pressIntensity,
|
||
duration: TIMING.clickDownDur,
|
||
ease: "power3.out", // spring(stiffness:300, damping:20)
|
||
},
|
||
TIMING.clickDownAt,
|
||
);
|
||
|
||
// RELEASE — back to scale 1.0
|
||
tl.to(
|
||
["#cta", "#cursor"],
|
||
{
|
||
scale: 1.0,
|
||
duration: TIMING.clickUpDur,
|
||
ease: "power2.out", // spring(stiffness:200, damping:15)
|
||
},
|
||
TIMING.clickUpAt,
|
||
);
|
||
</script>
|
||
</body>
|
||
</html>
|