mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-11 14:50:02 +00:00
feat(skills): c2v mining pass — 7 new blueprints, 10 new rules, compacted recipe corpus (#2680)
* feat(skills): c2v mining pass over animation blueprints and rules Compacts ~45 existing animation rules/blueprints into tighter recipe form (net -3.4k lines) and adds 17 mined from the c2v corpus: - 7 blueprints: agent-progress-theater, camera-journey, fixed-anchor-cycle, panel-edit-live-sync, prompt-type-submit-generate, transcript-scroll-artifact-reveal, zoom-out-workspace-reveal - 10 rules: 3d-camera-flight, anchored-layout-expand, chart-scrub-readout, chromatic-glitch, control-target-sync, cursor-drag, gradient-text-sweep, multi-cursor-choreography, particle-burst, theme-crossfade-morph Both indexes updated. * feat(skills): sync product-launch script bank with mined blueprint roles The role->blueprint script bank in product-launch-video/story-design.md is kept 1:1 with blueprints-index role declarations, which the c2v mining pass expanded. Adds the 25 missing entries (script-shape descriptor + example lines + pattern): 13 for the 7 new blueprints, 12 for role widenings on 7 existing ones (cursor-ui-demo, dataviz-countup, titlecard-reveal, et al.), and states the 1:1 sync contract in the bank's intro. * docs(skills): cover constellation-hub scatter-drift variant in the script bank Review follow-up on #2680: the SOCIAL_PROOF constellation-hub entry patterned only the orbit shape; the c2v pass added a scatter-drift end-card variant with the opposite geometry (no hub, no ring). Adds an example line and extends the pattern so a scatter-drift beat's VO isn't steered toward the orbit shape.
This commit is contained in:
@@ -7,344 +7,95 @@ metadata:
|
||||
|
||||
# Physics Press Reaction (Cursor + Element Synced)
|
||||
|
||||
Models a real click: a cursor approaches a button, lands, and both compress IN SYNC, then release together. Two distinct timing events (down-frame and up-frame) bound by spring forces. Distinct from [press-release-spring](press-release-spring.md) (which has no cursor — just a press happening); this rule is the COMBINED cursor + element behavior.
|
||||
Models a real click: a cursor approaches a button, lands, and both compress IN SYNC, then release together. Distinct from [press-release-spring.md](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.
|
||||
|
||||
## How It Works
|
||||
|
||||
A single `PRESS_INTENSITY` value drives both cursor and button together:
|
||||
|
||||
- **press down**: both compress to `1 - PRESS_INTENSITY`
|
||||
- **release**: both spring back to 1.0 with overshoot
|
||||
|
||||
The cursor ALSO translates to the button's center during the approach phase BEFORE press starts. After release, the cursor may move on (next interaction) or hold.
|
||||
|
||||
## HTML
|
||||
## Recipe
|
||||
|
||||
```html
|
||||
<div
|
||||
class="scene"
|
||||
id="press-react-scene"
|
||||
data-composition-id="press-react-scene"
|
||||
data-start="0"
|
||||
data-duration="3"
|
||||
data-track-index="0"
|
||||
>
|
||||
<div class="stack">
|
||||
<button class="btn" id="btn">
|
||||
<span class="btn-icon">{ctaIcon}</span>
|
||||
<span class="btn-label">{ctaCopy}</span>
|
||||
</button>
|
||||
<div class="brand">{Brand}</div>
|
||||
</div>
|
||||
<!-- Cursor lives at scene-root level so it can translate freely -->
|
||||
<svg class="cursor" id="cursor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M3 2 L21 12 L12 13 L7 22 Z"
|
||||
fill="{cursorFill}"
|
||||
stroke="{cursorStroke}"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<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>
|
||||
```
|
||||
|
||||
## CSS
|
||||
```js
|
||||
gsap.set("#cursor", { x: CURSOR_START_X, y: CURSOR_START_Y }); // off-screen / far corner
|
||||
|
||||
```css
|
||||
.scene {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: {sceneBg};
|
||||
font-family: {font};
|
||||
overflow: hidden;
|
||||
}
|
||||
.stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: STACK_GAP;
|
||||
}
|
||||
.btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: BTN_INNER_GAP;
|
||||
padding: BTN_PADDING_V BTN_PADDING_H;
|
||||
background: {btnBg};
|
||||
border: none;
|
||||
border-radius: BTN_RADIUS;
|
||||
color: {btnTextColor};
|
||||
font-family: {font};
|
||||
font-weight: 900;
|
||||
font-size: BTN_FONT_SIZE;
|
||||
letter-spacing: BTN_TRACKING;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
box-shadow: {btnRestingShadow};
|
||||
transform-origin: 50% 50%;
|
||||
will-change: transform;
|
||||
}
|
||||
.btn-icon {
|
||||
font-size: BTN_ICON_SIZE;
|
||||
line-height: 1;
|
||||
}
|
||||
.brand {
|
||||
font-size: BRAND_SIZE;
|
||||
font-weight: 800;
|
||||
letter-spacing: BRAND_TRACKING;
|
||||
color: {brandColor};
|
||||
text-transform: uppercase;
|
||||
}
|
||||
/* Cursor — absolute, positioned by GSAP */
|
||||
.cursor {
|
||||
position: absolute;
|
||||
width: CURSOR_SIZE;
|
||||
height: CURSOR_SIZE;
|
||||
pointer-events: none;
|
||||
z-index: 100;
|
||||
/* initial position is set by gsap.set() */
|
||||
transform-origin: 0 0; /* arrow point is the click point */
|
||||
filter: {cursorDropShadow};
|
||||
}
|
||||
```
|
||||
// Phase 1 — approach
|
||||
tl.to(
|
||||
"#cursor",
|
||||
{ x: BUTTON_CENTER_X, y: BUTTON_CENTER_Y, duration: APPROACH_DUR, ease: "power2.inOut" },
|
||||
APPROACH_START,
|
||||
);
|
||||
|
||||
## GSAP Timeline
|
||||
// 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,
|
||||
);
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
window.__timelines = window.__timelines || {};
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
// Phase 3 — release: both spring back together
|
||||
tl.to(
|
||||
["#btn", "#cursor"],
|
||||
{ scale: 1, duration: RELEASE_DUR, ease: `back.out(${BOUNCE_FACTOR})` },
|
||||
RELEASE_AT,
|
||||
);
|
||||
|
||||
// Position cursor initially off-target (off-screen or far corner).
|
||||
gsap.set("#cursor", { x: CURSOR_START_X, y: CURSOR_START_Y });
|
||||
// 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,
|
||||
);
|
||||
|
||||
// The button's screen center, in composition coordinates.
|
||||
const BUTTON_CENTER = { x: BUTTON_CENTER_X, y: BUTTON_CENTER_Y };
|
||||
|
||||
// Phase 1 — cursor approaches button
|
||||
tl.to(
|
||||
"#cursor",
|
||||
{
|
||||
x: BUTTON_CENTER.x,
|
||||
y: BUTTON_CENTER.y,
|
||||
duration: APPROACH_DUR,
|
||||
ease: "power2.inOut",
|
||||
},
|
||||
APPROACH_START,
|
||||
);
|
||||
|
||||
// Phase 2 — coordinated press down (button + cursor both scale to 1 - PRESS_INTENSITY)
|
||||
tl.to(
|
||||
["#btn", "#cursor"],
|
||||
{
|
||||
scale: 1 - PRESS_INTENSITY,
|
||||
duration: PRESS_DOWN_DUR,
|
||||
ease: "power1.in",
|
||||
},
|
||||
PRESS_DOWN_AT,
|
||||
);
|
||||
|
||||
// Phase 3 — release (both spring back to 1.0 with overshoot)
|
||||
tl.to(
|
||||
["#btn", "#cursor"],
|
||||
{
|
||||
scale: 1,
|
||||
duration: RELEASE_DUR,
|
||||
ease: `back.out(${BOUNCE_FACTOR})`,
|
||||
},
|
||||
RELEASE_AT,
|
||||
);
|
||||
|
||||
// Phase 4 — inner glow during press (boxShadow change synced to press scale)
|
||||
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,
|
||||
);
|
||||
|
||||
// Brand fades in early (context)
|
||||
tl.from(
|
||||
".brand",
|
||||
{ opacity: 0, y: BRAND_REVEAL_Y_PX, duration: BRAND_REVEAL_DUR, ease: "power3.out" },
|
||||
BRAND_REVEAL_AT,
|
||||
);
|
||||
|
||||
// Cursor optionally moves off after press (or holds for dwell)
|
||||
tl.to(
|
||||
"#cursor",
|
||||
{ x: CURSOR_EXIT_X, y: CURSOR_EXIT_Y, duration: CURSOR_EXIT_DUR, ease: "power2.out" },
|
||||
CURSOR_EXIT_AT,
|
||||
);
|
||||
|
||||
window.__timelines["press-react-scene"] = tl;
|
||||
</script>
|
||||
// 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
|
||||
- **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."
|
||||
|
||||
Cursor presses button A → button A triggers swap → cursor moves to button B → presses again. Each press is one full down-release sub-routine.
|
||||
## Values
|
||||
|
||||
### Hold press (continuous pressure)
|
||||
|
||||
Insert a `HOLD_DUR` window between press-down and release. Cursor scale stays at `1 - PRESS_INTENSITY`, button scale stays at `1 - PRESS_INTENSITY`, inner glow stays on. Suggests "thinking" or "loading."
|
||||
|
||||
### Synchronized inner-glow pulse
|
||||
|
||||
During the hold phase, the inner glow pulses (sin-driven). Suggests "processing":
|
||||
|
||||
```js
|
||||
const holdGlow = { p: 0 };
|
||||
tl.to(
|
||||
holdGlow,
|
||||
{
|
||||
p: Math.PI * GLOW_PULSE_CYCLES * 2,
|
||||
duration: HOLD_DUR,
|
||||
ease: "none",
|
||||
onUpdate: () => {
|
||||
const alpha = GLOW_BASE_ALPHA + Math.sin(holdGlow.p) * GLOW_PULSE_AMP;
|
||||
document.getElementById("btn").style.boxShadow =
|
||||
`inset 0 0 GLOW_BLUR rgba(255, 255, 255, ${alpha})`;
|
||||
},
|
||||
},
|
||||
HOLD_START_AT,
|
||||
);
|
||||
```
|
||||
|
||||
## How to Choose Values
|
||||
|
||||
### Timing (seconds)
|
||||
|
||||
- **APPROACH_START** — when the cursor begins moving toward the button.
|
||||
- Range: 0-0.3 s (small lead-in is fine; long delays read as a dead frame)
|
||||
- **APPROACH_DUR** — cursor approach duration.
|
||||
- Range: 0.7-1.3 s; faster reads as urgent, slower as deliberate
|
||||
- **PRESS_DOWN_AT** — when the press fires.
|
||||
- Constraints: MUST equal `APPROACH_START + APPROACH_DUR` so the cursor arrives exactly when the press begins (avoids "tapping on air")
|
||||
- **PRESS_DOWN_DUR** — compression duration.
|
||||
- Range: 0.1-0.25 s
|
||||
- **RELEASE_AT** — when the release fires.
|
||||
- Constraints: must be > `PRESS_DOWN_AT + PRESS_DOWN_DUR`; an optional brief hold (0.05-0.4 s, or `HOLD_DUR` for the Hold-press variation) for "thinking" interactions
|
||||
- **RELEASE_DUR** — release spring duration.
|
||||
- Range: 0.4-0.7 s (long enough for the overshoot to settle)
|
||||
- **BRAND_REVEAL_AT** — when the brand line fades in.
|
||||
- Constraints: must be < `PRESS_DOWN_AT` (context precedes interaction)
|
||||
- **BRAND_REVEAL_DUR** — brand fade-in duration.
|
||||
- Range: 0.4-0.8 s
|
||||
- **CURSOR_EXIT_AT / CURSOR_EXIT_DUR** — optional outbound cursor motion after release.
|
||||
- Constraints: `CURSOR_EXIT_AT` must be ≥ `RELEASE_AT + RELEASE_DUR` so the cursor exits AFTER the press settles, not during
|
||||
|
||||
### Physics
|
||||
|
||||
- **PRESS_INTENSITY** — how deep the press compression goes.
|
||||
- Range: 0.05 (subtle) - 0.10 (standard) - 0.15 (heavy)
|
||||
- Applied as `scale: 1 - PRESS_INTENSITY` on both cursor and button (single GSAP target array)
|
||||
- **BOUNCE_FACTOR** — `back.out(${BOUNCE_FACTOR})` overshoot on the release.
|
||||
- Range: 1.6 (soft) - 2.0 (firm) - 2.4 (cartoony)
|
||||
|
||||
### Positioning
|
||||
|
||||
- **CURSOR_START_X / CURSOR_START_Y** — initial cursor position in composition coordinates.
|
||||
- Constraints: off-screen or in a corner far from the button so the approach reads as motion-in, not a teleport
|
||||
- **BUTTON_CENTER_X / BUTTON_CENTER_Y** — the button's measured screen-space center.
|
||||
- Source: measured at composition coordinates; for `place-items: center` at 1920×1080 this is `(960, 540)`
|
||||
- **CURSOR_EXIT_X / CURSOR_EXIT_Y** — where the cursor moves after release (if used).
|
||||
- Range: any off-stage or out-of-the-way position
|
||||
- **BRAND_REVEAL_Y_PX** — brand initial y offset.
|
||||
- Range: 8-20 px
|
||||
|
||||
### Layout / typography
|
||||
|
||||
- **STACK_GAP** — gap between button and brand line.
|
||||
- Range: 40-96 px
|
||||
- **BTN_PADDING_V / BTN_PADDING_H** — button padding.
|
||||
- Range: V 24-40 px, H 60-100 px (horizontal padding 2-3× vertical reads as pill-shaped CTA)
|
||||
- **BTN_INNER_GAP** — gap between icon and label inside the button.
|
||||
- Range: 16-32 px
|
||||
- **BTN_RADIUS** — button corner radius.
|
||||
- Range: 20-40 px, or `BTN_PADDING_V + BTN_FONT_SIZE/2` for fully rounded ends
|
||||
- **BTN_FONT_SIZE / BTN_ICON_SIZE** — typographic sizes inside the button.
|
||||
- Range: font 60-100 px at 1080p; icon ~1.0-1.1× font size
|
||||
- **BTN_TRACKING** — letter-spacing on uppercase button text.
|
||||
- Range: 4-12 px
|
||||
- **BRAND_SIZE / BRAND_TRACKING** — brand line typography.
|
||||
- Range: 40-60 px, tracking 8-16 px
|
||||
- **CURSOR_SIZE** — cursor SVG size.
|
||||
- Range: 48-96 px at 1080p
|
||||
|
||||
### Hold-press variation
|
||||
|
||||
- **HOLD_DUR** — hold window between press down and release.
|
||||
- Range: 0.3-0.8 s
|
||||
- **HOLD_START_AT** — when the glow pulse begins.
|
||||
- Constraints: typically equal to `PRESS_DOWN_AT + PRESS_DOWN_DUR`
|
||||
- **GLOW_PULSE_CYCLES** — number of full sine cycles across `HOLD_DUR`.
|
||||
- Range: 1-4 (more cycles read as faster "processing")
|
||||
- **GLOW_BASE_ALPHA** — center of the alpha pulse.
|
||||
- Range: 0.15-0.3
|
||||
- **GLOW_PULSE_AMP** — peak deviation from `GLOW_BASE_ALPHA`.
|
||||
- Range: 0.1-0.2; must satisfy `GLOW_BASE_ALPHA - GLOW_PULSE_AMP ≥ 0`
|
||||
- **GLOW_BLUR** — inset glow blur radius (px).
|
||||
- Range: 24-48 px
|
||||
|
||||
### Tokens
|
||||
|
||||
- **{sceneBg}** — background gradient/color
|
||||
- **{font}** — typographic stack
|
||||
- **{btnBg}** — button background (typically gradient toward an accent hue)
|
||||
- **{btnTextColor}** — button text color
|
||||
- **{btnRestingShadow}** / **{btnPressedShadow}** — outer + inset box-shadow strings for the resting and pressed states
|
||||
- **{brandColor}** — accent brand color
|
||||
- **{cursorFill}** / **{cursorStroke}** — cursor SVG fill and stroke
|
||||
- **{cursorDropShadow}** — `filter: drop-shadow(...)` value for cursor depth
|
||||
- **{Brand}** — brand line copy
|
||||
- **{ctaCopy}** / **{ctaIcon}** — button label and inline icon glyph
|
||||
|
||||
## Key Principles
|
||||
|
||||
- **Same press scale on cursor AND button** — physical synchronicity. If only the button scales, the cursor appears to "tap on air"; if only the cursor scales, the button feels disconnected.
|
||||
- **Cursor arrives BEFORE press starts** — there must be a clear moment of "cursor over target" before scale change. Otherwise the press is unattributed.
|
||||
- **`back.out(${BOUNCE_FACTOR})` for release** — both elements need spring overshoot together. Linear release loses the tactile feel.
|
||||
- **Inner glow appears DURING press, fades on release** — visual confirmation of contact. Outer shadow shrinks (pushed-in), inner glow appears (energy concentrated).
|
||||
- **Cursor `pointer-events: none`** — the cursor is decorative; if it captures events, hover/click behaviors on button below break.
|
||||
- **Cursor `transform-origin: 0 0`** — the arrow's tip is the click point, not its center. Scale around the tip keeps the click point stable.
|
||||
- **Climax dwell ≥1 s** — after release, the comp must continue ≥1 s. The press is a beat; viewer needs time to see the result.
|
||||
| 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
|
||||
|
||||
- **Timeline must be paused**: `gsap.timeline({ paused: true })`
|
||||
- **Registry key = `data-composition-id`**
|
||||
- **No CSS `transition`** on either cursor or button — competes with GSAP
|
||||
- **Cursor SVG with `pointer-events: none`**
|
||||
- **`will-change: transform`** on button (and cursor if desired)
|
||||
- **`up-frame > down-frame`** — release MUST come after press; otherwise the comp shows release without press
|
||||
- **Don't use real `mouseenter` / `click` events** — HF is a render context, not a UI; everything must run via the timeline
|
||||
- **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.
|
||||
|
||||
## Combinations
|
||||
## See also
|
||||
|
||||
- [press-release-spring.md](press-release-spring.md) — the BUTTON-only press variant; this rule layers cursor on top
|
||||
- [cursor-click-ripple.md](cursor-click-ripple.md) — adds a ripple effect at the click point
|
||||
- [scale-swap-transition.md](scale-swap-transition.md) — the press TRIGGERS the swap
|
||||
|
||||
## Pairs with HF skills
|
||||
|
||||
- `/hyperframes-animation` — coordinated multi-target tweens via array
|
||||
- `/hyperframes-core` — composition wiring
|
||||
- `/hyperframes-cli` — `hyperframes lint`
|
||||
`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).
|
||||
|
||||
Reference in New Issue
Block a user