dj-transition / dj-remove — declarative animations

Phoenix JS.transition parity. Apply a class for the duration of a state change. The framework handles the timing.

Tasks

dj-transition-group
Wire up the WebSocket consumer
Style the dashboard
Read the @action docs

The whole pattern

dj-transition-group="ENTER | LEAVE" wires dj-transition onto each new child and dj-remove onto every child for exit. The framework cycles classes and waits for transitionend automatically — no JS needed in your view.

{# Each side is 3 space-separated classes — start, active, end. #}
{# Pipe (|) separates the enter spec from the exit spec.        #}
<ul dj-transition-group=
    "t-enter-from t-enter-active t-enter-to | t-exit-from t-exit-active t-exit-to">
    {% for task in tasks %}
        <li class="task">{{ task.title }}</li>
    {% endfor %}
</ul>

/* CSS — phase 1 sets start; framework swaps to active+end on next frame. */
/* 550ms drop-in with overshoot, scale, tilt, color flash.       */
/* (Framework caps animations at 600ms — see upstream issue.)    */
.t-enter-from   { opacity: 0;
                  transform: translateX(-140px) scale(0.6) rotate(-6deg);
                  background: hsl(var(--primary) / 0.45); }
.t-enter-active { transition:
                    opacity 550ms ease,
                    transform 550ms cubic-bezier(0.34, 1.56, 0.64, 1),
                    background 550ms ease; }
.t-enter-to     { opacity: 1; transform: none; }

Compare to React's <TransitionGroup> / <CSSTransition> from react-transition-group — three packages, build-time install, runtime overhead. Here it's an HTML attribute.