Motivation
Animation bugs are hard to debug from console warnings alone — the "Animation timeout reached" message tells you something went wrong but not what: which lifecycle phase stalled, which slot the element belongs to, whether the transition ever started, whether an animation group's gate was still closed, or how long each phase actually took. A DevTools view of live animations would collapse most of this into a glance.
What to expose
For every animation currently in progress or recently completed:
- Target element (with data-effex-key + slot identity)
- Kind: enter / exit
- Phase:
waiting-for-connection → waiting-for-paint (hydration only) → applying-enterFrom → reflow → applying-enterTo → waiting-for-event → cleanup → done / timeout
- Timing: how long spent in each phase
- Config: the resolved
AnimationOptions for this animation (enter, enterFrom, enterTo, exit, exitTo, timeout, stagger delay)
- Group membership: if
animate.group is set, which group + current pending count + gate state
- Ended by:
transition / animation / timeout / skip (from waitForAnimationEvent)
Recent completions (last N, ring buffer) are as valuable as in-flight ones — most animation issues manifest as "the animation ran but didn't do what I expected."
Instrumentation points
Roughly maps 1:1 to lifecycle boundaries we already have:
forkSlotEnter / forkSlotRemoval (packages/dom/src/Control/slotAnimation.ts) — start of an animation; emit a "created" event with target, kind, resolved config.
runEnterAnimation / runExitAnimation (packages/dom/src/Animation/core.ts) — phase transitions; emit between addBeforeReflow / forceReflow / removeAfterReflow / waitForAnimationEnd / removeAfterAnimation.
waitForAnimationEvent (packages/dom/src/Animation/helpers.ts) — emit which resolution path fired (transition / animation / timeout / skip).
- Animation groups (
packages/dom/src/Animation/groups.ts) — emit register / gate-open / complete transitions.
Proposed shape
Same opt-in pattern as #86: a Context.Tag for an AnimationInspector service, unused by default, an Instrumented layer that plumbs into window.__EFFEX_DEVTOOLS__.animations for the panel to read. Zero cost when the tag isn't provided.
Emit events rather than snapshots — the panel can build its own state model (in-flight vs. historical) rather than the runtime being responsible for retention semantics.
Considerations
- Overhead. Phase-transition emit is on every animation. Should be cheap (single function call) and ideally batched via
queueMicrotask if the panel needs it.
- Interruption. If a slot's scope closes mid-animation (route change during transition), the fiber is interrupted. That interruption should emit a phase change so the panel doesn't show a phantom in-flight animation forever.
- Correlation. Enter and exit animations on the same key (during a re-mount) should be groupable in the UI — need a stable key that survives the slot re-creation.
- Reduced-motion. Users with reduced-motion preference get
skip short-circuits. Panel should show these as "skipped (reduced motion)" rather than making them look like bugs.
Related
Motivation
Animation bugs are hard to debug from console warnings alone — the "Animation timeout reached" message tells you something went wrong but not what: which lifecycle phase stalled, which slot the element belongs to, whether the transition ever started, whether an animation group's gate was still closed, or how long each phase actually took. A DevTools view of live animations would collapse most of this into a glance.
What to expose
For every animation currently in progress or recently completed:
waiting-for-connection→waiting-for-paint(hydration only) →applying-enterFrom→reflow→applying-enterTo→waiting-for-event→cleanup→done/timeoutAnimationOptionsfor this animation (enter, enterFrom, enterTo, exit, exitTo, timeout, stagger delay)animate.groupis set, which group + current pending count + gate statetransition/animation/timeout/skip(fromwaitForAnimationEvent)Recent completions (last N, ring buffer) are as valuable as in-flight ones — most animation issues manifest as "the animation ran but didn't do what I expected."
Instrumentation points
Roughly maps 1:1 to lifecycle boundaries we already have:
forkSlotEnter/forkSlotRemoval(packages/dom/src/Control/slotAnimation.ts) — start of an animation; emit a "created" event with target, kind, resolved config.runEnterAnimation/runExitAnimation(packages/dom/src/Animation/core.ts) — phase transitions; emit betweenaddBeforeReflow/forceReflow/removeAfterReflow/waitForAnimationEnd/removeAfterAnimation.waitForAnimationEvent(packages/dom/src/Animation/helpers.ts) — emit which resolution path fired (transition / animation / timeout / skip).packages/dom/src/Animation/groups.ts) — emit register / gate-open / complete transitions.Proposed shape
Same opt-in pattern as #86: a
Context.Tagfor anAnimationInspectorservice, unused by default, anInstrumentedlayer that plumbs intowindow.__EFFEX_DEVTOOLS__.animationsfor the panel to read. Zero cost when the tag isn't provided.Emit events rather than snapshots — the panel can build its own state model (in-flight vs. historical) rather than the runtime being responsible for retention semantics.
Considerations
queueMicrotaskif the panel needs it.skipshort-circuits. Panel should show these as "skipped (reduced motion)" rather than making them look like bugs.Related