Enrichment manager modal: orb-style entrance (springy rise + cascading worker rail)

The modal opened with a plain pop — out of place next to the worker orbs. Now it
springs up from the bottom (toward the Manage Workers button) with the SAME easing
the orbs reveal with, then the worker rail assembles one-by-one: each chip springs
in staggered (scale 0.4→1) with a brief pulse of its own brand colour. Mirrors the
orb motion language AND walks your eye across every worker + its live state dot /
coverage bar as they land — cool + informative. Respects prefers-reduced-motion.
pull/784/head
BoulderBadgeDad 1 week ago
parent 25222bd8f7
commit 08dcd00217

@ -349,14 +349,16 @@ function _emRailSubText(status) {
function renderEnrichmentRail() {
const rail = document.getElementById('em-rail');
if (!rail) return;
rail.innerHTML = ENRICHMENT_WORKERS.map(w => {
rail.innerHTML = ENRICHMENT_WORKERS.map((w, i) => {
const status = enrichmentManagerState.statuses[w.id];
const info = _emStatusInfo(status);
const pct = _emOverallPct(status);
const cov = pct == null ? '' : `
<span class="em-rail-cov"><span class="em-rail-cov-fill" style="width:${pct}%"></span></span>`;
const accent = _emHexToRgb(w.color);
return `
<button class="em-worker-row" id="em-row-${w.id}"
style="--i:${i};--row-accent:${accent}"
onclick="selectEnrichmentWorker('${w.id}')">
${_emIconHtml(w.id)}
<span class="em-worker-meta">

@ -65165,10 +65165,37 @@ body.em-scroll-lock { overflow: hidden; }
.em-overlay { transition: opacity 0.22s ease, backdrop-filter 0.22s ease; }
.em-overlay.em-closing { opacity: 0; }
@keyframes em-pop-in {
from { opacity: 0; transform: translateY(14px) scale(0.97); }
from { opacity: 0; transform: translateY(34px) scale(0.9); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
.em-in { animation: em-pop-in 0.28s cubic-bezier(0.16, 1, 0.3, 1) both; }
/* Springy rise (same easing the worker orbs reveal with) + grows from the
bottom, toward the Manage Workers button it launched from. */
.em-in { animation: em-pop-in 0.42s cubic-bezier(0.34, 1.56, 0.64, 1) both; transform-origin: center bottom; }
/* The worker rail assembles like the orbs expanding: each chip springs in
(scale 0.41, same orb easing), staggered, with a brief pulse of its own
brand colour so opening the modal mirrors the orb motion AND walks your eye
across every worker + its live state as they land. */
@keyframes em-rail-reveal {
0% { opacity: 0; transform: translateX(-14px) scale(0.4); }
55% { opacity: 1; }
100% { opacity: 1; transform: translateX(0) scale(1); box-shadow: none; }
}
@keyframes em-rail-glow {
0%, 100% { box-shadow: none; }
45% { box-shadow: inset 0 0 0 1px rgba(var(--row-accent, 120,120,120), 0.6), 0 0 16px rgba(var(--row-accent, 120,120,120), 0.45); }
}
.em-in .em-worker-row {
animation:
em-rail-reveal 0.46s cubic-bezier(0.34, 1.56, 0.64, 1) both,
em-rail-glow 0.7s ease-out both;
/* modal settles first (~0.14s), then workers cascade in */
animation-delay: calc(0.14s + var(--i, 0) * 0.05s);
}
@media (prefers-reduced-motion: reduce) {
.em-in { animation-duration: 0.2s; }
.em-in .em-worker-row { animation: none; }
}
.em-overlay.em-closing .enrichment-manager-modal { transform: scale(0.98); opacity: 0; transition: all 0.16s ease; }
.enrichment-manager-modal:focus { outline: none; }

Loading…
Cancel
Save