Files
zooqueen 62bbc7766c skills: give each skill the directory its name declares
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.
2026-08-13 18:17:24 -07:00

6.4 KiB
Raw Permalink Blame History

name, description, metadata
name description metadata
reactive-displacement Physical collision where an entering element's spring drives the exiting element's displacement — single source of truth makes the motion causally linked.
tags
transition, physics, collision, displacement, spring, causal

Reactive Displacement

Exit animation of element A is mathematically DERIVED from the entry spring of element B — a causal link: "A moves because B hit it." Distinct from scale-swap-transition.md (which overlaps but isn't causal) and card-morph-anchor.md (one container morphing).

A single 0→1 driver tween (the "entry spring") feeds three concurrent derived motions in one onUpdate:

  • Intruder (B, entering): position interpolated off-stage → settled over the full driver, plus tilt settling to 0° and a sharp early opacity reveal.
  • Victim (A, exiting): position interpolated settled → off-stage in the OPPOSITE direction, completing at VICTIM_FRACTION (~0.4–0.5) of the driver — NOT 1.0.

The victim finishing BEFORE the intruder's entry creates the "hit then settle" rhythm; sharing one eased driver makes the impact moment mathematically synchronized.

Recipe

// Both cards absolutely centered; overflow: hidden on the scene (off-stage travel);
// will-change: transform, opacity on both; intruder z-index ABOVE victim.
const INTRUDER_START_X = STAGE_W; // off-stage right
const VICTIM_END_X = -STAGE_W; // off-stage left — SAME axis, opposite direction

gsap.set("#victim", { x: 0, opacity: 1, rotation: 0 });
gsap.set("#intruder", { x: INTRUDER_START_X, opacity: 0, rotation: -INTRUDER_TILT });

const driver = { p: 0 };
tl.to(
  driver,
  {
    p: 1,
    duration: DRIVER_DUR,
    ease: `back.out(${BOUNCE_FACTOR})`, // the intruder spring
    onUpdate: () => {
      // Intruder: full 0→1 progress maps enter (off-stage → center)
      const intruderX = INTRUDER_START_X * (1 - driver.p);
      const intruderOpacity = Math.min(1, driver.p * FADE_IN_SHARPNESS);
      const intruderRot = -INTRUDER_TILT * (1 - driver.p); // settles to 0°
      const intruder = document.getElementById("intruder");
      intruder.style.transform = `translate(-50%, -50%) translateX(${intruderX}px) rotate(${intruderRot}deg)`;
      intruder.style.opacity = String(intruderOpacity);

      // Victim: completes its exit at VICTIM_FRACTION of the driver — by the
      // time the intruder centers, the victim is already off-stage.
      const victimP = Math.min(1, driver.p / VICTIM_FRACTION);
      const victimX = VICTIM_END_X * victimP;
      const victim = document.getElementById("victim");
      victim.style.transform = `translate(-50%, -50%) translateX(${victimX}px)`;
      victim.style.opacity = String(1 - victimP);
    },
  },
  DRIVER_AT,
);
// Climax dwell — intruder holds centered for ≥ DWELL_MIN before the scene ends.

Variations

  • Impact rotation on victim — the victim also rotates as it slides: const victimRot = victimP * -VICTIM_KICK_DEG; appended to its transform. VICTIM_KICK_DEG 15–25°, magnitude matched to the perceived intruder weight.
  • Vertical collision — intruder from top, victim displaced downward; same math on Y. Reads as "weight dropped on it."
  • Wobble after settle — after the intruder centers, a damped sine wobble (±WOBBLE_AMP_DEG rotation, linearly decaying over WOBBLE_DUR via a second ease: "none" driver at DRIVER_AT + DRIVER_DUR) before stillness — "impact aftermath."
  • Multi-victim ripple — the intruder displaces multiple aligned cards, each victim's victimP on a slightly offset driver phase (cascade ripple).

Values

token range notes
DRIVER_AT phase-dependent after the prior reading beat resolves; must leave ≥ DWELL_MIN of climax dwell before the scene ends
DRIVER_DUR 0.6–1.4 s short = zippy punch, long = heavy landed impact; higher bounce on long durations reads as floaty
BOUNCE_FACTOR 1.2–2.0 (typ. 1.4–1.6) stay in the back.out family (or elastic.out for oscillation) — changing family rewrites the feel
VICTIM_FRACTION 0.4–0.5 <0.4 the victim disappears before the impact reads; >0.5 feels parallel, not causal; hard cap ~0.6
STAGE_W ≥ composition width smaller leaves the off-stage element partially visible at start
INTRUDER_TILT 5–15° (typ. ~10°) low = clean glide, high = "spin-and-plant"; sign consistent with entry direction (momentum transfer)
FADE_IN_SHARPNESS 3–8 intruder reaches opacity 1 at 1/FADE_IN_SHARPNESS of progress; must be > 1 or it's transparent at center
DWELL_MIN ≥ 1.0 s (typ. 1.0–1.5) post-impact dwell is where the new content gets read — do not skip

Critical Constraints

  • Single driver = single source of truth — both motions computed inside ONE driver's onUpdate, never separate tl.to() calls per element; independent tweens destroy the causal link (they'd merely be near each other in time).
  • Victim completes at a fraction of the driver — the "hit" is the overlap moment; after it the victim is just vacating space the intruder will fill.
  • Directional momentum transfer — same axis, opposite directions; different axes read as passing, not colliding.
  • Intruder z-index above victim — explicit, not DOM order; otherwise the victim looks like it tunneled through.
  • Intruder enters tilted, settles flat — small initial tilt → 0° reads as "spinning in then planting."
  • Climax dwell after impact — the impact is the headline beat; hold the settled intruder ≥ DWELL_MIN.
  • overflow: hidden on the scene — off-stage motion exceeds the frame.

See also

control-target-sync (the live-editing mirror — repeated coupled edits, nothing exits) · hacker-flip-3d (intruder text reveal during entry) · sine-wave-loop (idle breathing during the dwell) · vertical-spring-ticker (a ticker that "shoves" the previous content out).