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.
@action
dj-form-pending
@server_function
dj-transition
ActivityMixin
WizardMixin
dj_suspense
defer()
DataTableMixin
dj-virtual
dj-viewport
UploadMixin
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.