diff --git a/.agents/skills/hyperframes-animation/SKILL.md b/.agents/skills/hyperframes-animation/SKILL.md new file mode 100644 index 0000000..416c7c1 --- /dev/null +++ b/.agents/skills/hyperframes-animation/SKILL.md @@ -0,0 +1,82 @@ +--- +name: hyperframes-animation +description: "All animation knowledge for HyperFrames — atomic motion rules, multi-phase scene blueprints, scene transitions, broader motion-design techniques, AND the seven runtime adapters (GSAP default, plus Lottie, Three.js, Anime.js, CSS keyframes, Web Animations API, TypeGPU). Use for any motion or animation task: pick 2-4 rules and compose, or load a blueprint, or look up runtime-specific API (e.g. GSAP eases / Lottie player / Three.js mixer). HyperFrames-native: single paused timeline, seek-safe, deterministic." +--- + +# HyperFrames Animation + +All motion knowledge in one skill: **rules** (atomic recipes), **blueprints** (multi-phase scene templates), **transitions** (scene-to-scene), **techniques** (broader motion-design patterns), and **adapters** (per-runtime APIs). + +For the composition contract (data attributes, sub-compositions, determinism) see `hyperframes-core`. + +## Default: compose atomic rules + +Pick 2-4 rules from `rules-index.md`, glue them together with a single paused GSAP timeline, done. This is faster and produces less code than starting from a blueprint. + +## Load a blueprint when + +- The scene matches an existing pre-designed multi-phase template (brand-reveal, social-proof, etc.) and reusing its phase pipeline saves real authoring time +- You want runnable ground-truth code for a complex 4-5 phase choreography + +Blueprints live in `blueprints-index.md`. Each entry points to `blueprints/.md` (recipe). Do not read it speculatively; load it when you've already decided you need scene-level orchestration. + +## Routing + +| Want to… | Read | +| ------------------------------------------------------------------------------ | --------------------------------------------------- | +| Pick an atomic motion pattern by trigger / tag | `rules-index.md` | +| Read one rule's full HTML / CSS / GSAP recipe | `rules/.md` | +| Pick a multi-phase scene template | `blueprints-index.md` | +| Read one blueprint's full recipe | `blueprints/.md` | +| Author a scene transition (CSS-driven, between two clips) | `transitions/overview.md`, `transitions/catalog.md` | +| Look up a broader motion-design technique | `techniques.md` | +| Analyze an existing composition's animation map | `scripts/animation-map.mjs` | +| GSAP API — timeline / tweens / position parameters | `adapters/gsap.md` | +| GSAP — drop-in effect recipes | `rules/gsap-effects.md` | +| GSAP — transforms / perf | `adapters/gsap-transforms-and-perf.md` | +| GSAP — eases / stagger | `adapters/gsap-easing-and-stagger.md` | +| GSAP — timeline / labels | `adapters/gsap-timeline-and-labels.md` | +| Lottie / dotLottie (After Effects exports, `window.__hfLottie`) | `adapters/lottie.md` | +| Three.js / WebGL (3D scenes, `AnimationMixer`, `hf-seek`) | `adapters/three.md` | +| Anime.js (`window.__hfAnime`) | `adapters/animejs.md` | +| CSS keyframes (`animation-delay` / `play-state` / `fill-mode`) | `adapters/css-animations.md` | +| Web Animations API (`element.animate()`, `currentTime` seek) | `adapters/waapi.md` | +| TypeGPU / WebGPU (`navigator.gpu`, WGSL, compute pipelines) | `adapters/typegpu.md` | +| HTML-as-texture + WebGL/GLSL post-fx (capture live DOM via `drawElementImage`) | `adapters/html-in-canvas-patterns.md` | +| Named text-animation effects (24 IDs via external `animate-text` skill) | `adapters/animate-text.md` | + +## Picking a runtime + +- **GSAP** is the default for 95% of motion work — covers timeline orchestration, transforms, easing, stagger. All atomic rules in this skill are GSAP-based. +- **Lottie** when an asset has its own pre-baked timeline (typically After Effects exports). +- **Three.js** for 3D scenes, camera motion, shader-driven visuals. +- **Anime.js** for lightweight tweening when GSAP is overkill. +- **CSS** for simple repeated motifs, decoration, shimmer — no JavaScript animation cost. +- **WAAPI** for native browser keyframes without a GSAP dependency. +- **TypeGPU / WebGPU** for GPU-rendered canvases (particles, liquid glass, custom shaders). + +Multiple runtimes can coexist in one composition. Each registers its instances on the runtime-specific global so HyperFrames can seek all of them in one pass. + +## Critical Constraints + +**Prerequisite: `hyperframes-core` → Non-Negotiable Rules** (single paused timeline, `data-duration` governs length, no `Math.random` / `Date.now` / `performance.now`, no `repeat: -1`, no `gsap.set` on later-scene clips, no `display` / `visibility` animation, no timeline construction inside `async` / `setTimeout` / `Promise`). Don't restate those here. + +Animation-craft additions on top of core's contract: + +- **Pre-calculated layout constants** — never derive positions from `getBoundingClientRect()` at tween time. Tween-time DOM measurements desync because the renderer samples in parallel; compute coordinates once at composition setup and reuse. +- **Spatial motion uses GSAP transform aliases only** (`x`, `y`, `scale`, `rotation`). Core's allowlist also permits `opacity` / `color` / `backgroundColor` / `borderRadius` for non-spatial property tweens — but never `width` / `height` / `top` / `left` for layout changes. + +## Scripts + +```bash +node skills/hyperframes-animation/scripts/animation-map.mjs \ + --out /.hyperframes/anim-map +``` + +Reads every GSAP timeline registered on `window.__timelines`, enumerates tweens, samples bboxes, computes flags, outputs `animation-map.json`. Use it to audit choreography (dead zones, stagger consistency, lifecycle warnings) after authoring. + +## See Also + +- `hyperframes-core` — composition structure, data attributes, sub-compositions, deterministic render contract +- `hyperframes-creative` — palettes, typography, narration, beat planning (non-animation creative direction) +- `hyperframes-cli` — `npx hyperframes lint / validate / inspect / preview / render` diff --git a/.agents/skills/hyperframes-animation/adapters/animate-text.md b/.agents/skills/hyperframes-animation/adapters/animate-text.md new file mode 100644 index 0000000..d722d70 --- /dev/null +++ b/.agents/skills/hyperframes-animation/adapters/animate-text.md @@ -0,0 +1,64 @@ +# Text Effects — Reference + +For deterministic text-animation specs (e.g., `typewriter` at exact `240ms / 46ms stagger / steps(1, end) easing`), this skill defers to the separate **`animate-text`** skill maintained by Pixel Point at [github.com/pixel-point/animate-text](https://github.com/pixel-point/animate-text). It provides a catalog of 24 named text effects with portable contracts and per-library implementation recipes (GSAP, Anime.js, WAAPI). + +**We do NOT ship the catalog inside this repo.** Pixel Point's `animate-text` is the source of truth; vendoring its files here would violate the upstream's licensing (no explicit license declared upstream as of this writing). Loading the skill separately keeps the legal picture clean while giving you the same catalog. + +## How to use it + +When a beat needs a deterministic text animation, load the upstream skill alongside this one: + +```bash +# In your project root, install the upstream skill into .agents/skills/ +npx skills add pixel-point/animate-text +``` + +Or in a skill-aware agent runtime, the skill is invoked by name: + +``` +/animate-text +``` + +Once installed, the specs live at: + +``` +.agents/skills/animate-text/assets/effects/.json # per-library implementation recipe +.agents/skills/animate-text/assets/specs/.json # portable motion contract +``` + +Sub-agents reading those files get exact GSAP timings, easing strings, DOM split rules, and stagger algorithms — no creative invention needed. + +## When you don't need the upstream skill + +If a beat's text animation is simple enough to describe in prose ("headline fades up word-by-word, 80ms stagger"), implement it inline using the GSAP knowledge already in these skills (`hyperframes-creative` → `references/motion-principles.md` and `references/beat-direction.md`; `hyperframes-animation` → `techniques.md`, entry #4 "Per-Word Kinetic Typography"). The upstream catalog is most valuable when: + +- You want a specific NAMED effect across multiple beats (so they feel like one design system, not one-offs) +- You're choosing between several similar effects (typewriter vs per-character-rise vs bottom-up-letters) and want to see all 24 in one place +- You need layout-aware effects (`kinetic-center-build`, `short-slide-right`, `short-slide-down`) where parameters alone aren't enough — those ship with custom layout algorithms + +## Effect names — vocabulary (do NOT use this as the implementation source) + +For convenience while writing storyboards: the upstream skill provides 24 effects. Their IDs are listed here so you can name them in `STORYBOARD.md` even before loading the upstream skill. **The implementation specs are in the upstream skill, not here.** + +- **Per-character (7):** soft-blur-in, per-character-rise, typewriter, bottom-up-letters, top-down-letters, stagger-from-center, stagger-from-edges +- **Per-word (8):** per-word-crossfade, spring-scale-in, shared-axis-y, blur-out-up, kinetic-center-build, short-slide-right, short-slide-down, depth-parallax-words +- **Per-line (2):** mask-reveal-up, line-by-line-slide +- **Whole element (7):** micro-scale-fade, shimmer-sweep, fade-through, shared-axis-z, scale-down-fade, focus-blur-resolve, shared-axis-x + +For descriptions, durations, easing curves, and the per-library recipes: load `/animate-text` and read its own catalog page. + +## In the storyboard + +Every text element in every beat can name an effect by ID, e.g.: + +```markdown +**Text Animations:** + +- Main headline: `kinetic-center-build` +- Eyebrow label: `soft-blur-in` +- Body copy 3 lines: `mask-reveal-up` +``` + +Sub-agents implementing the beat will load `/animate-text` if it's not already loaded, then read the spec for each named effect from the upstream skill's files. + +If the upstream skill isn't available (offline build, network restrictions, agent runtime that doesn't support skill loading), sub-agents fall back to implementing the effect from the description alone — using GSAP knowledge plus the effect ID as a description of intent (e.g., "typewriter" = per-character stepped reveal with no interpolation). diff --git a/.agents/skills/hyperframes-animation/adapters/animejs.md b/.agents/skills/hyperframes-animation/adapters/animejs.md new file mode 100644 index 0000000..95a67a2 --- /dev/null +++ b/.agents/skills/hyperframes-animation/adapters/animejs.md @@ -0,0 +1,114 @@ +--- +name: hyperframes-animejs +description: Anime.js adapter patterns for HyperFrames. Use when writing Anime.js animations or timelines inside HyperFrames compositions, registering animations on window.__hfAnime, making Anime.js seek-driven and deterministic, or translating Anime.js examples into render-safe HyperFrames HTML. +--- + +# Anime.js for HyperFrames + +HyperFrames can seek Anime.js instances through its `animejs` runtime adapter. The composition owns the animation objects; HyperFrames owns the clock. + +## Contract + +- Create animations or timelines synchronously during composition initialization. +- Set `autoplay: false` so Anime.js does not advance on its own clock. +- Register every returned animation or timeline on `window.__hfAnime`. +- Use finite durations and loop counts. +- Avoid callbacks that mutate DOM based on wall-clock time, network state, or unseeded randomness. + +The adapter seeks every registered instance with `instance.seek(timeMs)`, where `timeMs` is HyperFrames time in milliseconds. + +## Basic Pattern + +```html + + +``` + +## Timeline Pattern + +```html + +``` + +## Module Builds + +If you use an ES module build, the adapter does not care how the instance was created. It only needs the returned object to expose `seek()`, `pause()`, and preferably `play()`: + +```html + +``` + +## Good Uses + +- Small SVG and DOM flourishes where Anime.js syntax is compact. +- Imported Anime.js examples that can be made seek-driven. +- Multiple independent micro-animations pushed into the same registry. + +Use GSAP for complex scene sequencing unless the user specifically asks for Anime.js. GSAP is still the primary HyperFrames authoring path. + +## Avoid + +- Leaving `autoplay` at the Anime.js default. +- Depending on `anime.running` auto-discovery instead of explicit `window.__hfAnime.push(...)`. +- Infinite loops. Compute a finite repeat count from the composition duration. +- Building animations in timers, promises, event handlers, or after async asset loads. + +## Validation + +After editing a composition that uses Anime.js: + +```bash +npx hyperframes lint +npx hyperframes validate +``` + +## Credits And References + +- HyperFrames adapter source: `packages/core/src/runtime/adapters/animejs.ts`. +- Anime.js documentation for `autoplay`, `pause()`, and `seek()`: https://animejs.com/documentation/ diff --git a/.agents/skills/hyperframes-animation/adapters/css-animations.md b/.agents/skills/hyperframes-animation/adapters/css-animations.md new file mode 100644 index 0000000..64a9167 --- /dev/null +++ b/.agents/skills/hyperframes-animation/adapters/css-animations.md @@ -0,0 +1,124 @@ +--- +name: hyperframes-css-animations +description: CSS animation adapter patterns for HyperFrames. Use when authoring CSS keyframes, animation-delay based timing, animation-fill-mode, animation-play-state, or CSS-only motion that HyperFrames must seek deterministically during preview and rendering. +--- + +# CSS Animations for HyperFrames + +HyperFrames can seek CSS keyframe animations through its `css` runtime adapter. Use this for simple repeated motifs, background motion, shimmer, glow, masks, and non-sequenced decoration. + +For scene choreography, GSAP is usually clearer. CSS animations work best when the motion belongs to one element and has a fixed duration. + +## Contract + +- Put the animated element in the DOM before runtime initialization finishes. +- Give timed elements a `data-start` value so local animation time matches the clip. +- Use finite `animation-duration` and `animation-iteration-count` because the negative-delay fallback cannot represent unbounded duration in environments without WAAPI-backed CSS animations. +- Prefer `animation-fill-mode: both` so seeked states hold before and after active motion. +- Avoid wall-clock JavaScript, hover-triggered state, and class toggles that depend on user events. + +The adapter discovers elements with computed `animation-name`, seeks their browser `Animation` handles when available, and falls back to pausing with negative `animation-delay`. + +## Basic Pattern + +```html +
+ + +``` + +## Stagger Pattern + +Use CSS custom properties to avoid duplicating keyframes: + +```html +
+ + + +
+ + +``` + +## Good Uses + +- Decorative loops with a known repeat count. +- Mask, glow, shimmer, grain, and subtle parallax layers. +- Simple one-element entrances where a full JS timeline would be excessive. + +## Avoid + +- Infinite CSS animations unless you have verified the browser exposes seekable WAAPI-backed CSS animation handles. Prefer a finite iteration count covering the visible duration. +- Animating layout properties like `top`, `left`, `width`, or `height` when transforms work. +- Relying on hover, focus, scroll, or media queries to trigger render-critical motion. +- Changing animation classes after startup unless another deterministic timeline controls that change. + +## Validation + +After editing CSS animation compositions: + +```bash +npx hyperframes lint +npx hyperframes validate +``` + +## Credits And References + +- HyperFrames adapter source: `packages/core/src/runtime/adapters/css.ts`. +- MDN CSS animation documentation: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/animation +- MDN `animation-fill-mode`: https://developer.mozilla.org/en-US/docs/Web/CSS/animation-fill-mode diff --git a/.agents/skills/hyperframes-animation/adapters/gsap-easing-and-stagger.md b/.agents/skills/hyperframes-animation/adapters/gsap-easing-and-stagger.md new file mode 100644 index 0000000..c3eb93d --- /dev/null +++ b/.agents/skills/hyperframes-animation/adapters/gsap-easing-and-stagger.md @@ -0,0 +1,118 @@ +# Easing, Stagger, and Function-Based Values + +## Easing + +Built-in eases: `power1`, `power2`, `power3`, `power4`, `back`, `bounce`, `circ`, `elastic`, `expo`, `sine`, `none`. + +Each has `.in`, `.out`, `.inOut` variants. + +| Ease | Use for | +| -------------------------- | ----------------------------------------------------------------------- | +| `power1.out`, `power2.out` | Standard UI motion. Default for most entrances. | +| `power3.out`, `power4.out` | Punchier deceleration. Title cards, hero reveals. | +| `sine.inOut` | Long, slow, calm motion. Crossfades, ambient drift. | +| `back.out(1.7)` | Slight overshoot. Playful entrances. The arg controls overshoot amount. | +| `elastic.out(1, 0.3)` | Springy bounce. First arg = amplitude, second = period. | +| `expo.inOut` | Snappy, dramatic. Quick transitions between hero scenes. | +| `none` (linear) | Camera moves with timed counterpoint, mechanical motion. | + +Pick `.out` for entrances, `.in` for exits, `.inOut` for symmetric moves and continuous motion. + +## Easing Vocabulary (character & mood) + +Easings are tone of voice: a video that only whispers is boring; one that varies between whisper, normal, and punch is engaging. Every composition should use at least 3 different easings — `power2.out` for everything produces flat, monotonous motion. + +The full palette by character (each family has `.in`, `.out`, `.inOut` variants): + +| Family | Character | Typical use | +| -------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | +| `power1`–`power4` | Gentle (1) to aggressive (4) acceleration curves | General purpose. power2 is the workhorse, power4 for dramatic snaps | +| `back(N)` | Overshoot then settle. N controls how far past the target (1=subtle, 4=wild) | Logo reveals, badge pops, card entrances. `back.out(2.5)` for playful, `back.out(1.2)` for elegant | +| `elastic(amp, freq)` | Spring bounce. amp=magnitude, freq=oscillation speed | Panel scatter, energetic drops, fun reveals | +| `bounce` | Ball-drop bouncing | Physical interactions, icons landing, score counters | +| `expo` | Extreme acceleration curve (much steeper than power4) | Premium/luxury reveals, dramatic entrances | +| `sine` | Smooth, organic, no hard edges | Ambient float, breathing, Ken Burns, anything that loops. `.inOut` for yoyo motion | +| `circ` | Circular acceleration (starts very fast, ends very gentle or vice versa) | Camera moves, scene transitions, orbital motion | +| `steps(N)` | Discrete N-step jumps, no interpolation | Typing effects, cursor blink, counter ticks, retro/digital aesthetics | + +**Mood mapping:** Match easing character to the beat's emotional content. Smooth/organic easings (`sine`, `power1`) feel contemplative and drifting. Aggressive deceleration (`power4.out`, `expo.out`) feels snappy and confident. Spring overshoot (`back.out`) feels bouncy and physical. The storyboard's mood description should guide which character fits — not a formula. + +## Defaults + +```javascript +const tl = gsap.timeline({ + paused: true, + defaults: { duration: 0.6, ease: "power2.out" }, +}); +``` + +Or globally: + +```javascript +gsap.defaults({ duration: 0.6, ease: "power2.out" }); +``` + +Setting defaults at timeline scope is preferred — it documents the motion language of that composition in one place. + +## Stagger + +```javascript +gsap.fromTo(".item", { y: 24, opacity: 0 }, { y: 0, opacity: 1, duration: 0.5, stagger: 0.08 }); +``` + +Object form: + +```javascript +gsap.fromTo( + ".item", + { y: 24, opacity: 0 }, + { + y: 0, + opacity: 1, + stagger: { + each: 0.08, // delay between each + from: "center", // "start" | "end" | "center" | "edges" | "random" | index + amount: 0.6, // total stagger time (overrides each if both set) + grid: "auto", // for 2D stagger + axis: "x" | "y", + }, + }, +); +``` + +Prefer `stagger` over N separate tweens with manual delays — it stays correct when the target count or order changes. Use `fromTo()` rather than `from()` so the start state is explicit (see `gsap-timeline-and-labels.md` → sub-composition entrances). + +## Function-Based Values + +Any var can be a function `(index, target, targets) => value`: + +```javascript +gsap.to(".item", { + x: (i, target, targets) => i * 50, + rotation: (i) => (i % 2 === 0 ? 5 : -5), + stagger: 0.1, +}); +``` + +Use this for per-element values that depend on index, attributes, or measured size. Cheaper and more idiomatic than building tweens in a loop. + +## gsap.matchMedia (preview only) + +`matchMedia` runs setup only when a media query matches and auto-reverts when it stops matching. It is useful for **preview** in the browser at different viewport sizes, and for `prefers-reduced-motion`. It is **not** a substitute for rendering at the composition's actual `data-width`/`data-height` — HyperFrames renders at a fixed viewport. + +```javascript +let mm = gsap.matchMedia(); +mm.add( + { + isDesktop: "(min-width: 800px)", + reduceMotion: "(prefers-reduced-motion: reduce)", + }, + (context) => { + const { isDesktop, reduceMotion } = context.conditions; + gsap.to(".box", { + rotation: isDesktop ? 360 : 180, + duration: reduceMotion ? 0 : 2, + }); + }, +); +``` diff --git a/.agents/skills/hyperframes-animation/adapters/gsap-timeline-and-labels.md b/.agents/skills/hyperframes-animation/adapters/gsap-timeline-and-labels.md new file mode 100644 index 0000000..48c47dc --- /dev/null +++ b/.agents/skills/hyperframes-animation/adapters/gsap-timeline-and-labels.md @@ -0,0 +1,96 @@ +# Timelines and Labels + +HyperFrames is a seek-driven runtime. Build one paused timeline per composition, attach it to `window.__timelines[""]`, and let HyperFrames seek it. Never call `.play()` for render-critical motion. + +## Creating a Timeline + +```javascript +const tl = gsap.timeline({ + paused: true, + defaults: { duration: 0.5, ease: "power2.out" }, +}); + +tl.to(".a", { x: 100 }).to(".b", { y: 50 }).to(".c", { opacity: 0 }); +``` + +Timeline options: + +- **paused: true** — required in HyperFrames. The framework drives the playhead. +- **repeat**, **yoyo** — apply to the whole timeline. `repeat: -1` is forbidden; use finite counts. +- **defaults** — vars merged into every child tween. Use this instead of repeating `ease` and `duration` on every line. + +## Position Parameter + +The third argument to `.to()`/`.from()`/`.fromTo()` controls placement on the timeline: + +| Form | Meaning | +| -------------- | ------------------------------------ | +| `0`, `1.5` | Absolute time in seconds | +| `"+=0.5"` | 0.5s after the end of the timeline | +| `"-=0.2"` | 0.2s before the end of the timeline | +| `"intro"` | At the `intro` label | +| `"intro+=0.3"` | 0.3s after the `intro` label | +| `"<"` | Same start as the previous tween | +| `">"` | Right after the previous tween ends | +| `"<0.2"` | 0.2s after the previous tween starts | +| `">-0.1"` | 0.1s before the previous tween ends | + +```javascript +tl.to(".a", { x: 100 }, 0); +tl.to(".b", { y: 50 }, "<"); // same start as .a +tl.to(".c", { opacity: 0 }, "<0.2"); // 0.2s after .b starts +``` + +Prefer the position parameter over `delay:` — it composes naturally and survives refactors that re-order tweens. + +## Labels + +```javascript +tl.addLabel("intro", 0); +tl.to(".a", { x: 100 }, "intro"); + +tl.addLabel("outro", "+=0.5"); +tl.to(".a", { opacity: 0 }, "outro"); +``` + +Labels make a long timeline readable and let multiple tweens converge on the same beat without re-typing absolute times. + +## Nesting Timelines + +```javascript +const master = gsap.timeline({ paused: true }); + +const child = gsap.timeline(); +child.to(".a", { x: 100 }).to(".b", { y: 50 }); + +master.add(child, 0); +``` + +In HyperFrames, **do not** nest sub-composition timelines into the host. Sub-compositions loaded via `data-composition-src` are seeked independently by HyperFrames from their own `data-start`. Nesting is only for grouping pieces of the _same_ composition's timeline. + +## Inside Sub-Compositions: prefer `fromTo` over `from` + +For entrance tweens inside a sub-composition, prefer `gsap.fromTo()` over `gsap.from()`: + +```javascript +// Sub-composition entrance — survives re-seek cleanly +tl.fromTo(".title", { y: 60, opacity: 0 }, { y: 0, opacity: 1, duration: 0.6 }, 0.2); +``` + +Why: HyperFrames re-seeks the sub-composition every time its host clip becomes visible. `gsap.from()` snapshots the starting state at **registration time** (page load); when the playhead jumps back past `data-start`, that snapshot can desync from the actual CSS state and the element renders in the wrong position. `gsap.fromTo()` declares both endpoints explicitly, so the seek-back always produces the same start state. + +In top-level (standalone) compositions either form works — there's no re-seek-through-mount cycle. + +## Playback Control (debug / preview only) + +```javascript +tl.play(); +tl.pause(); +tl.reverse(); +tl.restart(); +tl.time(2); +tl.progress(0.5); +tl.kill(); +``` + +These are useful when previewing in the browser. In rendered output HyperFrames calls `seek()` internally — your timeline must produce identical state for the same time value every time it is seeked. diff --git a/.agents/skills/hyperframes-animation/adapters/gsap-transforms-and-perf.md b/.agents/skills/hyperframes-animation/adapters/gsap-transforms-and-perf.md new file mode 100644 index 0000000..2955891 --- /dev/null +++ b/.agents/skills/hyperframes-animation/adapters/gsap-transforms-and-perf.md @@ -0,0 +1,96 @@ +# Transforms and Performance + +## Transform Aliases + +Prefer GSAP's transform aliases over raw `transform` strings: + +| GSAP property | Equivalent | +| --------------------------- | --------------------- | +| `x`, `y`, `z` | `translateX/Y/Z` (px) | +| `xPercent`, `yPercent` | `translateX/Y` in `%` | +| `scale`, `scaleX`, `scaleY` | `scale` | +| `rotation` | `rotate` (deg) | +| `rotationX`, `rotationY` | 3D rotate | +| `skewX`, `skewY` | `skew` | +| `transformOrigin` | `transform-origin` | + +Aliases let GSAP track and interpolate each axis independently, which prevents accidental overwrites between separate tweens on the same element. + +## autoAlpha + +Prefer `autoAlpha` over `opacity` for show/hide: + +```javascript +gsap.to(".panel", { autoAlpha: 0, duration: 0.4 }); +``` + +`autoAlpha: 0` sets both `opacity: 0` and `visibility: hidden`, which removes the element from hit-testing and accessibility tree at zero alpha — closer to "gone" than plain `opacity: 0`. + +## clearProps + +Removes inline styles set by GSAP when the tween completes: + +```javascript +gsap.to(".item", { x: 100, rotation: 45, clearProps: "all" }); +gsap.to(".item", { x: 100, rotation: 45, clearProps: "rotation,x" }); +``` + +Useful at the end of an animation segment to hand the element back to CSS. + +## CSS Variables + +```javascript +gsap.to(".chart", { "--hue": 180, duration: 1 }); +``` + +Animate any custom property. Works for color, length, number — anything CSS will interpolate. + +## Relative and Directional Values + +- Relative: `"+=20"`, `"-=10"`, `"*=2"`. +- Directional rotation: `"360_cw"`, `"-170_short"`, `"90_ccw"` — controls which way the angle takes when going between two values. + +## SVG Specifics + +- `svgOrigin` sets transform origin in the SVG's global coordinate space (not the element's local box). **Do not** combine `svgOrigin` with `transformOrigin` on the same element — pick one. +- Animate SVG transform attributes via the same alias names (`x`, `y`, `rotation`) — GSAP handles the SVG-specific quirks. + +## Performance Rules + +### Prefer transforms and opacity + +Animating `x`, `y`, `scale`, `rotation`, `opacity` stays on the GPU compositor. Avoid `width`, `height`, `top`, `left`, `margin`, `padding` when transforms achieve the same effect. + +### will-change (sparingly) + +```css +.title { + will-change: transform; +} +``` + +Only on elements that _actually_ animate. Applied everywhere it becomes useless and burns memory. + +### gsap.quickTo for frequent updates (preview-only) + +For high-frequency updates driven by **events** — pointer move, scroll, audio scrub — `quickTo` reuses the same tween instead of creating a new one each frame: + +```javascript +const xTo = gsap.quickTo("#cursor", "x", { duration: 0.4, ease: "power3" }); +const yTo = gsap.quickTo("#cursor", "y", { duration: 0.4, ease: "power3" }); + +container.addEventListener("mousemove", (e) => { + xTo(e.pageX); + yTo(e.pageY); +}); +``` + +> **Render mode has no input events.** The renderer seeks frame-by-frame; `mousemove`, `scroll`, etc. never fire. `quickTo`'s main use case applies in **live preview** in the browser only. For audio-reactive motion in renders, pre-extract audio data and drive the timeline declaratively (see `../rules/gsap-effects.md`). + +### Stagger beats N tweens + +One tween with `stagger` beats N tweens with manual delays for both readability and runtime cost. + +### Cleanup + +In live preview, pause or `kill()` off-screen animations. Render mode is unaffected (the renderer drives time directly). diff --git a/.agents/skills/hyperframes-animation/adapters/gsap.md b/.agents/skills/hyperframes-animation/adapters/gsap.md new file mode 100644 index 0000000..014da7b --- /dev/null +++ b/.agents/skills/hyperframes-animation/adapters/gsap.md @@ -0,0 +1,105 @@ +--- +name: hyperframes-gsap-adapter +description: GSAP animation API reference for HyperFrames. Use when writing seekable GSAP timelines in HyperFrames compositions, including gsap.to(), from(), fromTo(), set(), timeline position parameters, labels, easing, stagger, finite repeats, and transform performance. +--- + +# HyperFrames GSAP + +GSAP usage scoped to HyperFrames' seek-driven render model. This skill is the GSAP reference _as constrained by HyperFrames_ — for the framework's broader composition contract see `hyperframes-core`. + +## HyperFrames Contract + +HyperFrames controls GSAP through its `gsap` runtime adapter. Create a paused timeline synchronously, register it on `window.__timelines` with the exact `data-composition-id`, and let HyperFrames seek it. + +```html + + +``` + +- The registry key must match the composition root's `data-composition-id`. +- Bracket and dot syntax both register: `window.__timelines["main"] = tl` and `window.__timelines.main = tl` are equivalent (the linter recognizes both). Bracket form is required when the id isn't a valid identifier (e.g. contains `-`). +- Do not call `tl.play()` for render-critical motion. +- Do not build timelines inside async code, timers, or event handlers. +- Keep loops finite. HyperFrames renders finite video durations. +- **Render duration comes from `data-duration` on the composition root, not from GSAP timeline length.** Do not pad the timeline with empty tweens like `tl.set({}, {}, 283)` to "extend" it. (Some external docs show this trick; in HyperFrames it conflicts with the seek-driven duration model — set `data-duration` instead.) + +## Core Tween Methods + +- **gsap.to(targets, vars)** — animate from current state to `vars`. Most common. +- **gsap.from(targets, vars)** — animate from `vars` to current state (entrances). +- **gsap.fromTo(targets, fromVars, toVars)** — explicit start and end. +- **gsap.set(targets, vars)** — apply immediately (duration 0). + +Always use **camelCase** property names (e.g. `backgroundColor`, `rotationX`). + +## Common vars (cheatsheet) + +- **duration** — seconds (default 0.5). +- **delay** — seconds before start. +- **ease** — `"power1.out"` (default), `"power3.inOut"`, `"back.out(1.7)"`, `"elastic.out(1, 0.3)"`, `"none"`. See `./gsap-easing-and-stagger.md`. +- **stagger** — number or object. See `./gsap-easing-and-stagger.md`. +- **repeat** — finite number; never `-1` in HyperFrames. Compute repeats from the visible duration. +- **yoyo** — alternates direction with repeat. +- **overwrite** — `false` (default), `true`, or `"auto"`. +- **immediateRender** — default `true` for from()/fromTo(). Set `false` on later tweens targeting the same property+element. +- **onComplete**, **onStart**, **onUpdate** — callbacks. + +For transforms, autoAlpha, clearProps, and SVG specifics see `./gsap-transforms-and-perf.md`. + +## Animated Property Allowlist + +HyperFrames is stricter than vanilla GSAP. Animate only: + +- **Compositor-cheap**: `opacity`, `x`, `y`, `scale`, `scaleX`, `scaleY`, `rotation`, `rotationX`, `rotationY`, `skewX`, `skewY`, `transformOrigin` +- **Visual fills**: `color`, `backgroundColor`, `borderColor`, `borderRadius` +- **CSS variables**: `"--hue": 180` etc. +- **Media `volume`** (on `