Lists, transitions, and the DOM
Add purposeful transitions
Use motion to clarify state change while keeping duration modest and honoring reduced motion.
A transition has one job: help the user see what just appeared or disappeared. A panel that fades in for 150 milliseconds reads as “this opened”. A panel that pops in reads as “something changed, somewhere”.
That’s the whole justification. If a transition does more than that, it’s decoration, and decoration slows people down.
The one-word version
Alpine’s x-transition works on any element with x-show. Add the attribute and you get a default fade and scale:
<div x-show="expanded" x-transition>
Reported by Marta, 3 days ago.
</div>
You can tune it with modifiers. .opacity fades only, .duration.150ms sets the time:
<div x-show="expanded" x-transition.opacity.duration.150ms>
For the issue panel this is what I use. Opacity is cheap for the browser to animate, and 150ms is long enough to register and short enough to ignore.
Class-based transitions
When you need control, Alpine lets you set classes for each phase. enter classes apply while entering, enter-start at the first frame, enter-end at the last:
<div x-show="expanded"
x-transition:enter="fade"
x-transition:enter-start="fade-out"
x-transition:enter-end="fade-in"
x-transition:leave="fade"
x-transition:leave-start="fade-in"
x-transition:leave-end="fade-out">
.fade { transition: opacity 150ms ease-out; }
.fade-out { opacity: 0; }
.fade-in { opacity: 1; }
More verbose, but the timing now lives in CSS, which matters for the next section.
Honor reduced motion
Some people turn on “Reduce Motion” in their operating system. The browser exposes it as a media query. With class-based transitions, one rule turns everything off:
@media (prefers-reduced-motion: reduce) {
.fade { transition: none; }
}
Now the panel appears instantly for those users. Same content, same state, no animation. Test it: on macOS the setting is in System Settings, Accessibility, Display. Toggle it and reload.
The modifier form sets inline styles, which this CSS can’t override. That’s why I switch to classes as soon as a project needs reduced-motion support, which is every project.
Animate the cheap properties
Opacity and transform are cheap. The browser composites them without laying out the page again. Height, width, margin, and top trigger layout on every frame and stutter on slow devices.
If you want a panel to slide open, animate transform: translateY() on the panel, not its height.
Focus never waits
One more rule. If opening the editor moves focus into it, focus moves now, not after the fade ends. Keyboard users shouldn’t wait 150ms to start typing. The transition is visual. State, focus, and aria-expanded all update on the same tick.
Try both settings on your board: normal and reduced motion. In both, press Edit and start typing immediately. The characters should land in the field either way.
Lesson completed