In NOVA LAB's advanced systems, we need sophisticated effects — cascading dashboard element loading, animation on first page display, precise duration control, and respect for users' motion preferences. Learn the patterns that give interfaces a professional feel.
By default,
<Transition> only animates the element when toggling v-if/v-show. The appear attribute causes the animation to also run on the first render (mount) of the component:1<Transition appear name="fade">
2 <div>This element animates on page load</div>
3</Transition>This is the ideal solution for elements visible right after loading — the station logo, dashboard header, status panel. Without
appear, these elements would simply "be there" without any enter animation.You can use special CSS classes for the appear animation (independent from the normal enter classes):
1<Transition
2 appear
3 appear-from-class="appear-start"
4 appear-active-class="appear-running"
5 appear-to-class="appear-end"
6>
7 <div>Custom appear animation</div>
8</Transition>Or with libraries:
1<Transition
2 appear
3 appear-active-class="animate__animated animate__fadeInDown"
4>
5 <header>NOVA LAB Control Center</header>
6</Transition>One of the most impressive patterns — cascading appearance of list elements, where each successive element appears with an increasing delay. It looks like data displaying on a NOVA LAB holographic dashboard, element by element:
1<TransitionGroup
2 appear
3 :css="false"
4 @before-enter="onBeforeEnter"
5 @enter="onEnter"
6 tag="div"
7>
8 <div v-for="(item, index) in items" :key="item.id" :data-index="index">
9 {{ item.name }}
10 </div>
11</TransitionGroup>The key is the
:data-index attribute — it passes the element index to the JS hook, which calculates the delay:1function onBeforeEnter(el) {
2 el.style.opacity = 0
3 el.style.transform = 'translateY(20px)'
4}
5
6function onEnter(el, done) {
7 // Each successive element has a 150ms greater delay
8 const delay = el.dataset.index * 150
9
10 setTimeout(() => {
11 el.style.transition = 'all 0.5s ease'
12 el.style.opacity = 1
13 el.style.transform = 'translateY(0)'
14 el.addEventListener('transitionend', done, { once: true })
15 }, delay)
16}The effect: element 0 appears immediately, element 1 after 150ms, element 2 after 300ms, etc. For 10 elements, the entire animation takes 1.5s + 0.5s (animation duration itself) = ~2s.
Tip: Limit the delay to a max of ~1s so the user doesn't wait too long for the last elements:
1const delay = Math.min(el.dataset.index * 100, 1000)When an element has both CSS transition and animation, or when nested elements have different animation durations, Vue may not correctly detect the end point. The
:duration attribute lets you explicitly specify the duration in milliseconds:1<!-- Same duration for enter and leave -->
2<Transition :duration="500">...</Transition>
3
4<!-- Different durations for enter and leave -->
5<Transition :duration="{ enter: 300, leave: 500 }">...</Transition>This is particularly useful when:
Respect users' motion settings. People with vestibular disorders or epilepsy may have the
prefers-reduced-motion preference enabled. Your CSS should honor this:1/* Normal animations */
2.fade-enter-active,
3.fade-leave-active {
4 transition: opacity 0.3s ease, transform 0.3s ease;
5}
6
7.fade-enter-from,
8.fade-leave-to {
9 opacity: 0;
10 transform: translateY(10px);
11}
12
13/* Disable animations for users who don't want them */
14@media (prefers-reduced-motion: reduce) {
15 .fade-enter-active,
16 .fade-leave-active {
17 transition: none;
18 }
19
20 .fade-enter-from,
21 .fade-leave-to {
22 opacity: 1;
23 transform: none;
24 }
25}You can also handle this in JavaScript:
1const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
2
3function onEnter(el, done) {
4 if (prefersReducedMotion) {
5 done() // Skip the animation
6 return
7 }
8 // Normal animation...
9}Only animate
and transform
— these properties are GPU-accelerated and don't cause reflow/repaint. Avoid animating opacity
width, height, top, left, margin, padding.Use
for heavy animations — it informs the browser about an upcoming animation, allowing for optimization:will-change
1.heavy-animation-enter-active {
2 will-change: transform, opacity;
3 transition: all 0.5s ease;
4}Prefer CSS over JS — CSS animations are optimized by the browser and run on a separate thread. Use JS hooks only when you need dynamic values.
Short durations — UI animations should last 200-500ms. Longer animations slow down interaction and frustrate users.
Test on mobile devices — animations that run smoothly on desktop may lag on older phones.