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

9.6 KiB
Raw Permalink Blame History

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.
tags
glow, bloom, ambient, radial, sweep, hero, presence, finite, un-triggered

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:

  1. Hero bloom — a bounded idle breathe during the hold: a finite ease: "none" tween advances a phase proxy and onUpdate nudges opacity + scale a hair around peak (never a yoyo loop). sin(0) = 0 → the breathe starts exactly at the bloom's resting state.
  2. Traveling sweep — a narrow highlight band at one edge translates once across to the other (x off-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 fromTo is 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_START by ~0.15–0.3s across a row; shrink OPACITY_AMP / SCALE_AMP per the /√N rule below.
  • Diagonal raked sweep — angle {sweepGradient} (~105°) across a wordmark: the classic one-pass logo sheen. Narrower SWEEP_WIDTH, higher SWEEP_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 boxShadow glow 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).