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

5.4 KiB
Raw Permalink Blame History

name, description, metadata
name description metadata
scale-swap-transition Coordinated shrink-out + spring pop-in morph-like transition between two elements — no SVG path interpolation needed.
tags
transition, morph, scale, swap, spring, pop

Scale-Swap Transition

Simulates a "morph" between two DOM elements by overlapping exit and entrance scale animations. Lighter weight than card-morph-anchor.md (which morphs container dimensions — use that for SHAPE changes; this rule is for SAME-shape state swaps) and easier than SVG path interpolation.

At a single trigger, two coordinated tweens fire:

  1. Outgoing: scale 1.0 → EXIT_SCALE + opacity 1 → 0, fast power2.in (rushing away).
  2. Incoming: scale EXIT_SCALE → 1.0 + opacity 0 → 1, back.out(BOUNCE_FACTOR) (arriving with weight).

A small OVERLAP window during which both are mid-tween creates the morph illusion; the incoming sits on top via z-index so the outgoing's fade-tail doesn't bleed through.

Recipe

<!-- Both cards position: absolute; inset: 0 in one fixed-size wrapper — same
     footprint, same transform-origin: 50% 50%. Incoming starts opacity: 0,
     transform: scale(EXIT_SCALE), z-index above the outgoing. -->
<div class="swap-wrap">
  <div class="card outgoing" id="outgoing">{outgoingIcon} {outgoingLabel}</div>
  <div class="card incoming" id="incoming">
    {incomingIcon} {incomingLabel}
    <div class="sub" id="sub">{incomingSubline}</div>
  </div>
</div>
// Outgoing: shrink + fade fast
tl.to(
  "#outgoing",
  { scale: EXIT_SCALE, opacity: 0, duration: EXIT_DUR, ease: "power2.in" },
  TRIGGER,
);

// Incoming: pops in with overshoot, starting OVERLAP before the exit finishes
tl.to(
  "#incoming",
  { scale: 1.0, opacity: 1, duration: ENTER_DUR, ease: `back.out(${BOUNCE_FACTOR})` },
  TRIGGER + EXIT_DUR - OVERLAP,
);

// Inner content reveals AFTER the incoming settles
tl.fromTo(
  "#sub",
  { opacity: 0, y: SUB_REVEAL_Y_PX },
  { opacity: 1, y: 0, duration: SUB_REVEAL_DUR, ease: "power3.out" },
  TRIGGER + EXIT_DUR + SUB_REVEAL_DELAY,
);

Variations

  • Delayed inner content reveal — the classic pattern above: morph the container, then reveal inner text once it settles; the 0.2–0.4 s gap lets the eye land on the new shape before reading.
  • Triple swap (3-state cycle) — chain A→B→C with triggers TRIGGER_AB / TRIGGER_BC; each transition is its own tween pair, the previous incoming becoming the next outgoing. State-evolution narratives (early → mid → final labels).
  • Color-shift transition (no scale) — for a flat morph between same-shape states, drop the scale and keep opacity + a brief background hue tween; less dramatic, more product-UI tone.

Values

token range notes
TRIGGER ≥ outgoing settled + a presence-dwell the outgoing must "land" before transforming
EXIT_DUR 0.3–0.5 s
ENTER_DUR 0.45–0.7 s longer than EXIT_DUR so the overshoot can settle
OVERLAP 0.1–0.2 s >0.3 s both are clearly visible together (no morph); <0.05 s leaves a visible empty gap
EXIT_SCALE 0.6–0.8 smaller exits feel dramatic but risk reading as "vanish" instead of "morph"
BOUNCE_FACTOR 1.4 soft · 1.8 firm · 2.2 cartoony
SUB_REVEAL_DELAY 0.2–0.4 s reveals during the morph compete with the swap for attention
BRAND_REVEAL_AT < TRIGGER context (brand, eyebrow) sets the stage early; revealed AT the swap it competes with the headline beat

Critical Constraints

  • Incoming z-index ABOVE outgoing — otherwise the outgoing's fade-tail (opacity 0.3–0.5) bleeds through and double-exposes the frame.
  • Both elements share transform-origin: 50% 50% — different origins make the morph read as one thing teleporting elsewhere.
  • Bouncy ease ONLY on the incoming — outgoing power2.in, incoming back.out; reversed, the swap feels mechanical.
  • Both cards position: absolute; inset: 0 in the same fixed-size wrapper (sized to fit both states; the wrap never resizes).
  • Don't display: none the outgoing after the fade — leave it at opacity: 0 so layout doesn't reflow.
  • Inner content reveals after the container settles; climax dwell ≥ 1 s after the final state + subline land.

See also

press-release-spring (a button press TRIGGERS the swap — cause and effect) · card-morph-anchor (shape-changing alternative) · reactive-displacement (when the replacement should read as a causal collision) · sine-wave-loop (idle breathing on the final state).