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.
6.9 KiB
name, description, metadata
| name | description | metadata | ||
|---|---|---|---|---|
| press-release-spring | Tactile button press with linear compression, spring-based elastic recovery, and layered visual feedback (shadow shrink + release burst + background glow). |
|
Press-Release Spring Chain
Separates input (linear compression) from output (spring recovery) to create tactile feel: the overshoot is a natural byproduct of the spring config, not manually coded, with secondary motion (shadow shrink, release burst, background glow) layered on the same trigger frame. This is a reaction on an element already resting on screen — an arrival that springs in from nothing is spring-pop-entrance.md; add a visible cursor actor and it becomes physics-press-reaction.md.
Two phases split at the release:
- Press: linear ease → compression (
scale: 1 → PRESS_SCALE, shadow shrinks). Linear, not spring — the dip must read as instant/tactile, not squishy. - Release:
back.out(BOUNCE_FACTOR)spring back to 1.0. Optional burst glow ring expands behind the button; optional environmental glow fades in.
State continuity is critical: the release tween's start value MUST equal the press tween's end value, or the spring snaps to a different position. GSAP threads this automatically when both tweens target the same property at adjacent positions — RELEASE_START = PRESS_START + PRESS_DUR; a gap or overlap breaks it.
Recipe
<div class="press-stage">
<div class="bg-glow" id="bg-glow"></div>
<!-- Burst sits BEHIND the button (z-index 1 vs 2), same footprint, blurred
radial gradient, opacity 0. bg-glow is a full-stage radial at negative
inset so it extends past the stage edges. -->
<div class="burst" id="burst"></div>
<button class="btn" id="btn">{buttonLabel}</button>
</div>
// Phase 1 — press (linear compression)
tl.to(
"#btn",
{ scale: PRESS_SCALE, boxShadow: "{btnPressedShadow}", duration: PRESS_DUR, ease: "power1.in" },
PRESS_START,
);
// Phase 2 — release (spring back; start scale == PRESS_SCALE by adjacency)
tl.to(
"#btn",
{
scale: 1,
boxShadow: "{btnRestShadow}",
duration: RELEASE_DUR,
ease: `back.out(${BOUNCE_FACTOR})`,
},
RELEASE_START,
);
// Phase 3 — burst glow pops behind the button, then fades
tl.fromTo(
"#burst",
{ scale: 1, opacity: 0 },
{
scale: BURST_PEAK_SCALE,
opacity: BURST_PEAK_OPACITY,
duration: BURST_GROW_DUR,
ease: "power2.out",
},
RELEASE_START,
);
tl.to("#burst", { opacity: 0, duration: BURST_FADE_DUR, ease: "power2.in" }, BURST_FADE_START);
// Phase 4 — environmental glow fades in after release
tl.to(
"#bg-glow",
{ opacity: BG_GLOW_PEAK_OPACITY, duration: BG_GLOW_FADE_DUR, ease: "power2.out" },
RELEASE_START,
);
Variations
- Subtle press (status save / muted CTA):
PRESS_SCALE~0.96,BOUNCE_FACTOR~1.4, burst scale/opacity reduced. - Dramatic press (hero CTA / "ship it"):
PRESS_SCALE~0.88,BOUNCE_FACTOR~2.5, burst maxed. - Color shift during press — darken mid-press, return on release; interpolated
backgroundColorat the same timeline positions as the scale tweens. Same state-continuity rule. - State change at release (approve / confirm) — instead of returning to the rest color, swap to
{successColor}atRELEASE_STARTand pop a checkmark via a separateback.out(CHECK_BOUNCE)tween (1.4–2.0, firmer than the button's bounce — a punctuating "stamp"; pop 0.3–0.6 s) at the same position. The button is now terminal — no further presses expected.
Values
| token | range | notes |
|---|---|---|
| button footprint | ≥ 3–5% of canvas area | a 320×68 button at 1080p is ~1% and the press reads as visually insignificant |
| PRESS_SCALE | 0.88 dramatic · 0.92 default · 0.96 subtle | never <0.85 (broken) or >0.98 (no perceptible dip) |
| PRESS_DUR | 0.10–0.30 s | shorter = snappier; must be shorter than RELEASE_DUR (input faster than spring recovery) |
| RELEASE_DUR | 0.40–0.90 s | shorter = tight pop; longer = loose, wobbly settle |
| BOUNCE_FACTOR | 1.4 soft · 2.0 firm · 2.8 cartoony | or elastic.out(amplitude, period) for a rubbery oscillation instead of one overshoot |
| RELEASE_START | = PRESS_START + PRESS_DUR |
adjacency = automatic state continuity |
| BURST_PEAK_SCALE | 3 subtle · 6 default · 8 max | beyond ~8 the radial gradient pixelates visibly |
| BURST_PEAK_OPACITY | 0.4–1.0 | grow ≈ fade, 0.4–0.7 s each; blur 40–100 px (hard ring → ambient haze) |
| BG_GLOW_PEAK_OPACITY | 0.1 subtle · 0.25 default · 0.45 max | higher washes the whole composition; fade-in 0.6–1.0 s; inset −300…−500 px at 1080p |
Color tokens: pressed surface darker than rest; rest shadow large + diffuse, pressed small + tight (the button "sinks toward the surface"); burst gradient darker + more saturated than {btnBg} — same-color glow looks washed out; bg glow a low-opacity tint of the button's hue family.
Critical Constraints
- State continuity — release start value exactly equals press end value; enforced by same-property adjacency at
RELEASE_START = PRESS_START + PRESS_DUR. - Linear press, spring release — both spring → squishy; both linear → mechanical, no overshoot punch.
- Anchor compression on center (
transform-origin: 50% 50%) or the button collapses asymmetrically. - Burst behind, not in front — burst
z-index: 1, buttonz-index: 2; in front it occludes the button at peak opacity. - Don't tween
boxShadowandfilteron the same element — they compete in the layout pipeline; shadow on the button, blur on the separate burst layer. - Climax dwell — after the burst peak + reveal, the composition must run ≥ 1 s more (≥ 2 s for dramatic variants); a reveal at
t = DURATION − 0.2 sreads as "flashed and gone."
See also
spring-pop-entrance (the ENTRANCE counterpart — arrival, not reaction) · physics-press-reaction (this press with a visible cursor actor) · cursor-click-ripple (the cursor click that triggers the press) · sine-wave-loop (idle micro-float BEFORE the press) · center-outward-expansion (badge burst synced to the release).