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.5 KiB
Raw Permalink Blame History

name, description, metadata
name description metadata
physics-press-reaction Cursor + element synchronized press via subtractive spring forces — cursor lands on element, both compress together, then release. Distinct from press-release-spring (which has no cursor).
tags
spring, click, physics, cursor, subtractive, interaction, synchronized

Physics Press Reaction (Cursor + Element Synced)

Models a real click: a cursor approaches a button, lands, and both compress IN SYNC, then release together. Distinct from press-release-spring.md (no cursor — just a press happening); this rule is the COMBINED cursor + element behavior. A single PRESS_INTENSITY drives both: press down compresses both to 1 - PRESS_INTENSITY via one targets array, release springs both back to 1.0 with overshoot. The cursor translates to the button's center BEFORE the press starts; after release it may move on or hold.

Recipe

<button class="btn" id="btn">{ctaCopy}</button>
<!-- Cursor at scene-root level so it translates freely; arrow TIP is the click
     point, so transform-origin: 0 0 — scaling around the tip keeps it stable. -->
<svg class="cursor" id="cursor" style="pointer-events: none; transform-origin: 0 0">…</svg>
gsap.set("#cursor", { x: CURSOR_START_X, y: CURSOR_START_Y }); // off-screen / far corner

// Phase 1 — approach
tl.to(
  "#cursor",
  { x: BUTTON_CENTER_X, y: BUTTON_CENTER_Y, duration: APPROACH_DUR, ease: "power2.inOut" },
  APPROACH_START,
);

// Phase 2 — coordinated press down: ONE targets array, same scale
tl.to(
  ["#btn", "#cursor"],
  { scale: 1 - PRESS_INTENSITY, duration: PRESS_DOWN_DUR, ease: "power1.in" },
  PRESS_DOWN_AT,
);

// Phase 3 — release: both spring back together
tl.to(
  ["#btn", "#cursor"],
  { scale: 1, duration: RELEASE_DUR, ease: `back.out(${BOUNCE_FACTOR})` },
  RELEASE_AT,
);

// Phase 4 — inner glow during press, resting shadow on release (contact confirmation)
tl.to(
  "#btn",
  { boxShadow: "{btnPressedShadow}", duration: PRESS_DOWN_DUR, ease: "power1.in" },
  PRESS_DOWN_AT,
);
tl.to(
  "#btn",
  { boxShadow: "{btnRestingShadow}", duration: RELEASE_DUR, ease: "power2.out" },
  RELEASE_AT,
);

// Cursor optionally exits after the press settles
tl.to(
  "#cursor",
  { x: CURSOR_EXIT_X, y: CURSOR_EXIT_Y, duration: CURSOR_EXIT_DUR, ease: "power2.out" },
  CURSOR_EXIT_AT,
);

Variations

  • Multiple-element chain press — press button A → A triggers a swap → cursor moves to button B → presses again; each press is one full down-release sub-routine.
  • Hold press (continuous pressure) — insert a HOLD_DUR window between press-down and release: both scales stay at 1 - PRESS_INTENSITY, inner glow stays on. Suggests "thinking" or "loading."
  • Synchronized inner-glow pulse — during the hold, pulse the inset glow with a sine driver: a { p: 0 } proxy tweened to Math.PI * GLOW_PULSE_CYCLES * 2 on ease: "none", onUpdate writing boxShadow with alpha = GLOW_BASE_ALPHA + sin(p) * GLOW_PULSE_AMP. Suggests "processing."

Values

token range / rule notes
APPROACH_START 0–0.3 s long delays read as a dead frame
APPROACH_DUR 0.7–1.3 s faster = urgent, slower = deliberate
PRESS_DOWN_AT = APPROACH_START + APPROACH_DUR cursor arrives exactly as the press begins — avoids "tapping on air"
PRESS_DOWN_DUR 0.1–0.25 s
RELEASE_AT > PRESS_DOWN_AT + PRESS_DOWN_DUR optional 0.05–0.4 s hold (or HOLD_DUR 0.3–0.8 s) for "thinking" interactions
RELEASE_DUR 0.4–0.7 s long enough for the overshoot to settle
PRESS_INTENSITY 0.05 subtle · 0.10 standard · 0.15 heavy applied to both cursor and button via the single targets array
BOUNCE_FACTOR 1.6 soft · 2.0 firm · 2.4 cartoony
CURSOR_START / EXIT off-screen or far corner the approach must read as motion-in, not a teleport; exit ≥ RELEASE_AT + RELEASE_DUR
BUTTON_CENTER measured for place-items: center at 1920×1080: (960, 540)
BRAND_REVEAL_AT < PRESS_DOWN_AT context precedes interaction
glow pulse 1–4 cycles; base α 0.15–0.3; amp 0.1–0.2 GLOW_BASE_ALPHA − GLOW_PULSE_AMP ≥ 0
CURSOR_SIZE 48–96 px at 1080p

Critical Constraints

  • Same press scale on cursor AND button (one targets array) — only the button scaling makes the cursor "tap on air"; only the cursor scaling makes the button feel disconnected.
  • Cursor arrives BEFORE the press starts — a clear "cursor over target" moment, or the press is unattributed.
  • back.out(BOUNCE_FACTOR) on the release, for both together — a linear release loses the tactile feel; release MUST come after press.
  • Inner glow appears DURING press, fades on release — outer shadow shrinks (pushed in), inner glow appears (energy concentrated).
  • Cursor transform-origin: 0 0 — the arrow's tip is the click point; scale around the tip keeps it stable. pointer-events: none on the cursor.
  • Climax dwell ≥ 1 s — after release the composition must continue ≥ 1 s; the press is a beat, the viewer needs time to see the result.
  • No real mouseenter / click events — HF is a render context; everything runs via the timeline.

See also

press-release-spring (the BUTTON-only press; this rule layers the cursor on top) · cursor-click-ripple (adds a ripple at the click point) · scale-swap-transition (the press TRIGGERS the swap).