Files
frames/skills/frames-animation/rules/center-outward-expansion.md
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

4.2 KiB
Raw Permalink Blame History

name, description, metadata
name description metadata
center-outward-expansion Elements start clustered at screen center and expand outward to their final positions, driven by a shared progress value.
tags
expansion, scatter, center, reveal, layout, sync, burst

Center-Outward Expansion

Elements begin at one shared center point and radiate outward to their final positions — the entry beat itself, or motion driven by another animation's progress (a counting number, a beat). Flat 2D cousin of depth-scatter-assemble.md (per-element 3D cloud): here every element shares the SAME origin.

How It Works

Each element carries its final offset as data-target-x/y. Its position lerps between center and target: x = targetX × progress. Self-centering is baked as xPercent/yPercent: -50 so the tweened x/y are pure offsets from the stage center. Standalone burst = per-item staggered fromTo; driven burst = one shared proxy (see Variations).

Recipe

<!-- inside a standard scene clip (frames-core) -->
<div class="burst-wrap">
  <div class="burst-item" data-target-x="-360" data-target-y="-180">{itemA}</div>
  <div class="burst-item" data-target-x="360" data-target-y="-180">{itemB}</div>
  <div class="burst-item" data-target-x="0" data-target-y="360">{itemC}</div>
</div>
.burst-wrap {
  position: relative;
  width: 100%;
  height: 100%;
  display: grid;
  place-items: center;
}
.burst-item {
  position: absolute;
  top: 50%;
  left: 50%; /* GSAP xPercent/yPercent -50 bakes the centering; x/y tween the offset */
  will-change: transform;
}
document.querySelectorAll(".burst-item").forEach((el, i) => {
  tl.fromTo(
    el,
    { xPercent: -50, yPercent: -50, x: 0, y: 0, scale: 0.6, opacity: 0 },
    {
      x: Number(el.dataset.targetX),
      y: Number(el.dataset.targetY),
      scale: 1,
      opacity: 1,
      duration: EXPAND_DUR,
      ease: EXPAND_EASE,
    },
    ENTRY_AT + i * STAGGER,
  );
});

Variations

  • Synced to a driver (chord): when the burst shadows a counter / beat, drop the stagger and drive all items from ONE 0→1 proxy tween with the driver's exact duration AND ease; onUpdate writes translate(-50%,-50%) translate(targetX*p, targetY*p) per item — the two read as one beat.
  • Partially-spread start: with 6+ items the full cluster piles up — start from { x: targetX * START_PROGRESS, ... }.
  • Idle micro-float: hand off to sine-wave-loop.md after landing instead of freezing.

Values

token range notes
ITEM_COUNT 3–8 > 8 = visual chaos mid-expansion; low counts want wider spread
EXPAND_DUR 1.0–1.8s must equal the driver's duration in the synced variant
EXPAND_EASE power3.out default power2.out gentler, expo.out dramatic stop; NEVER in eases
STAGGER 0.04–0.08s tighter = chord; looser = lazy arpeggio
ENTRY_AT 0–0.5s a beat of compositional quiet before the burst
START_PROGRESS 0–0.5 0 = dramatic full cluster; ~0.3 avoids the pile-up

Critical Constraints

  • Tween x/y over the baked xPercent/yPercent: -50 — mutating left/top fights the centering and causes pixel jitter.
  • Out-easing only — in easings read as items being sucked back mid-air.
  • No other absolute-positioned siblings inside .burst-wrap — they'd steal the centered baseline.
  • ❗ The burst IS the beat — don't park a "real headline" label below it (the eye snaps to the label and ignores the burst). If a label is needed, reveal it post-burst in the same stack.
  • Synced variant: identical duration + ease as the driver, or the chord falls apart.

See also

counting-dynamic-scale (the classic chord driver) · depth-scatter-assemble (3D per-element cloud) · card-morph-anchor (burst out of a morphed card) · sine-wave-loop (post-landing life).