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.
9.6 KiB
name, description, metadata
| name | description | metadata | ||
|---|---|---|---|---|
| ambient-glow-bloom | Un-triggered soft radial glow that blooms in behind a hero element and holds with a bounded idle breathe, or a single-pass traveling sweep across a surface. No click, no word-sync — it just blooms. Finite, deterministic, seek-safe. |
|
Ambient Glow Bloom
A soft radial glow that blooms in behind a hero element (card, logo, metric) and holds, giving it presence. Unlike press-release-spring's click-triggered burst or asr-keyword-glow's word-timed envelope, this glow is un-triggered — it blooms on the hero's settle and stays lit. Two forms: a hero bloom that swells behind a settling element then breathes, and a traveling sweep that translates a soft highlight across a surface exactly once.
How It Works
A radial-gradient layer sits behind the hero (glow z-index: 1, hero z-index: 2 — a glow in front occludes it), starting at opacity: 0. Over the bloom-in window it ramps opacity: 0 → peak with a gentle scale swell, timed so BLOOM_START + BLOOM_DUR lands on the hero's settle — glow and hero resolve as ONE beat ("powering on"), never glow-then-card. After bloom-in:
- Hero bloom — a bounded idle breathe during the hold: a finite
ease: "none"tween advances aphaseproxy andonUpdatenudges opacity + scale a hair around peak (never ayoyoloop).sin(0) = 0→ the breathe starts exactly at the bloom's resting state. - Traveling sweep — a narrow highlight band at one edge translates once across to the other (
xoff-surface to off-surface), clipped to the surface (overflow: hidden). One pass, no return — a repeating sweep reads as a loading shimmer, not a reveal accent (the shimmer-sweep variation below is the sanctioned exception).
Peak opacity stays restrained (≤ 0.45 hard ceiling) so the glow gives presence without washing the frame; the glow color is darker + more saturated than the element it backs (a same-hue, same-lightness glow disappears into the surface).
Recipe
<!-- inside a standard scene clip -->
<div class="bloom-stage">
<div class="bloom-glow" id="bloom-glow"></div>
<!-- z-index: 1; inset: GLOW_INSET (negative); background: {glowGradient} -->
<div class="hero-card" id="hero-card">{HeroLabel}</div>
<!-- z-index: 2 -->
</div>
<!-- sweep form: <div class="sweep" id="sweep"> inside the overflow:hidden surface -->
// ── Form A: HERO BLOOM ── bloom in soft, landing on the hero's settle.
tl.fromTo(
"#bloom-glow",
{ opacity: 0, scale: GLOW_START_SCALE },
{ opacity: GLOW_PEAK_OPACITY, scale: 1, duration: BLOOM_DUR, ease: "power2.out" },
BLOOM_START,
);
// Bounded breathe during the hold — finite phase tween, NOT a yoyo loop.
const glow = document.getElementById("bloom-glow");
const phase = { p: 0 };
tl.to(
phase,
{
p: Math.PI * 2 * BREATHE_CYCLES,
duration: BREATHE_DUR,
ease: "none",
onUpdate: () => {
const s = Math.sin(phase.p);
glow.style.opacity = String(GLOW_PEAK_OPACITY + s * OPACITY_AMP);
glow.style.transform = `scale(${1 + s * SCALE_AMP})`;
},
},
BLOOM_START + BLOOM_DUR,
);
// ── Form B: TRAVELING SWEEP ── one finite pass, constant glide.
tl.fromTo(
"#sweep",
{ x: SWEEP_START_X, opacity: 0 },
{ x: SWEEP_END_X, opacity: SWEEP_PEAK_OPACITY, duration: SWEEP_DUR, ease: "none" },
SWEEP_START,
);
tl.to("#sweep", { opacity: 0, duration: SWEEP_FADE_DUR, ease: "power1.in" }, SWEEP_FADE_START);
Variations
- Bloom-and-hold — for scenes <3s or a hero with its own idle, skip the breathe: the single
fromTois the whole recipe. - Pulse-on-arrival — bloom slightly PAST peak (
GLOW_OVERSHOOT_OPACITY,scale: 1.06), then a second adjacent tween eases down to a steady hold — one breath punctuating the landing, no ongoing loop. - Multi-hero relay — stagger per-glow
BLOOM_STARTby ~0.15–0.3s across a row; shrinkOPACITY_AMP/SCALE_AMPper the/√Nrule below. - Diagonal raked sweep — angle
{sweepGradient}(~105°) across a wordmark: the classic one-pass logo sheen. NarrowerSWEEP_WIDTH, higherSWEEP_PEAK_OPACITY.
Shimmer sweep (text-clipped status-phrase working-state)
The sweep re-aimed inside type: a soft highlight gradient clipped into a status phrase ("Thinking…", "Analyzing dataset…") via background-clip: text travels left→right through the letterforms — the grey-on-grey shimmer that says still working. Unlike every other form here it legitimately repeats while the status is live: the repetition is diegetic working-state, not idle wobble (same defense as a blinking caret — the motion performs status). Two things keep it honest: it is bounded (one finite tween whose pass count is computed from the status window, never repeat: -1), and it is killed at resolve — the moment the status completes, the shimmer stops dead; a shimmer surviving into the answer beat turns a working indicator into decoration.
// Status shimmer — N passes as ONE bounded tween. Killed at resolve.
const status = document.getElementById("status-phrase");
// CSS on #status-phrase: background: {shimmerGradient}; background-size: 300% 100%;
// -webkit-background-clip: text; background-clip: text; color: transparent;
const shimmer = { p: 0 };
const PASSES = Math.round(STATUS_DUR / PASS_PERIOD); // whole passes, computed up front
tl.to(
shimmer,
{
p: PASSES,
duration: STATUS_DUR,
ease: "none",
onUpdate: () => {
const t = shimmer.p % 1; // 0→1 within each pass; percent axis inverted → left→right travel
status.style.backgroundPosition = `${(1 - t) * 100}% 50%`;
},
},
STATUS_START,
);
tl.set(status, { backgroundPosition: "100% 50%" }, STATUS_START + STATUS_DUR); // resolve: dead.
Keep it a whisper: {shimmerGradient} is the status text's own grey with one slightly-lighter band (highlight stop a step above the base, nothing near white); background-size ~300% keeps the band narrow in the glyphs; PASS_PERIOD 1.2–1.8s — slower reads as a sheen accent, faster as a spinner. Whole-number PASSES lands the band at its start position exactly at the kill frame, so the tl.set is visually a no-op. This is the working-state cousin of gradient-text-sweep: reach here when the sweep means "in progress," there when the gradient is the typographic treatment itself.
Values
| token | range / default | notes |
|---|---|---|
| GLOW_PEAK_OPACITY | 0.15 (subtle) → 0.30 (default) → 0.45 hard ceiling | higher washes the frame; a glow you consciously notice is too strong |
| GLOW_INSET | −200 to −450px (1920×1080) | negative so the halo extends past the hero; too small reads as a tight rim |
| GLOW_START_SCALE | 0.80–1.0 | ≤1.0 — grow into place, never shrink |
| BLOOM_DUR / BLOOM_START | 0.6–1.4s | BLOOM_START + BLOOM_DUR ≈ the hero's settle frame |
| OPACITY_AMP / SCALE_AMP | 0.02–0.05 / 0.01–0.03 default | PEAK + OPACITY_AMP ≤ 0.45; push only when the glow is the sole motion |
| BREATHE_CYCLES | period 2.5–4s per breath | glow breathes slower than element breathing |
| SWEEP_WIDTH | 15–35% of surface (grid) / 8–15% (wordmark) | |
| SWEEP_DUR | 0.8–1.6s | one deliberate pass — slow enough to read as light |
| SWEEP_PEAK_OPACITY | 0.10 → 0.25 (default) → 0.40 | same ≤ ~0.45 wash limit; tight sweeps tolerate the high end |
| SWEEP_START_X / END_X | fully off-surface both ends | no visible spawn/despawn mid-surface; fade reaches 0 as the band clears |
| PASS_PERIOD (shimmer) | 1.2–1.8s | with whole-number PASSES |
Critical Constraints
- Glow peak opacity ≤ 0.45 — including breathe amplitude; default to the LOW end (0.15–0.30).
- Glow behind, hero in front; glow color darker + more saturated than the hero surface.
- Land glow and hero as one beat — before or after reads as two separate events.
- Breathe is bounded, sweep is one pass — the only sanctioned repetition is the shimmer sweep, bounded and killed at resolve.
- Concurrent halos compound — per-glow amps ≤ default
/√N, stagger breathe periods (2.6s / 2.9s / 3.3s) so they don't pulse in lockstep. - Don't combine a
boxShadowglow on the hero with this halo layer — they compete and read muddy; the glow lives on the dedicated layer.
See also
sine-wave-loop (hero breathes on scale/y while the glow breathes on opacity, out of phase) · press-release-spring (the click-triggered sibling — never both behind one element) · counting-dynamic-scale / stat-bars-and-fills (bloom behind a landing stat) · center-outward-expansion (sweep across the assembled grid) · gradient-text-sweep (the design-beat gradient counterpart).