Motion
Merchants spend entire workdays in the admin completing business-critical tasks. Motion in this context is feedback, not decoration. It confirms that a click registered, shows where an element came from, and keeps state changes from feeling abrupt. Users should notice what changed, never the animation itself.
When in doubt, do not animate. A fast, well-eased 150ms transition beats an elaborate 500ms sequence every time.
Principles
- Minimal. Use only as much motion as necessary. Every movement has a clear purpose, so users can focus on their work rather than the interface.
- Purposeful. Every animation answers a question: did my click register, where did this element come from, what just changed. If it answers no question, cut it.
- Calm. Motion in Meteor is professional and quiet. Avoid playful, bouncy, or hectic movement. Save expressiveness for rare moments such as onboarding or empty states, never for everyday interactions.
Marketing surfaces follow their own motion guidelines with longer, more expressive timing. Those values are designed for storytelling and do not transfer to product interfaces.
When to animate
Decide based on how often a user triggers the change:
| Frequency | Example | Motion |
|---|---|---|
| Many times per hour | Navigating lists, moving selection, toggling table rows | None, or 100ms at most |
| Keyboard-initiated | Focus movement, arrow-key navigation, shortcuts | Never animate |
| Occasional | Opening a modal, popover, or drawer | Standard, under 400ms |
| Rare | Onboarding, empty states, success moments | May be slightly more expressive |
High-frequency actions must feel instant. Animating them makes the interface feel disconnected from the user's input, and the delay compounds hundreds of times a day. Click through both lists quickly, or hold an arrow key, to feel the difference:
Duration
| Element | Duration |
|---|---|
| Hover, press, color, and shadow changes | 100-150ms |
| Tooltips, dropdowns, popovers | 150-250ms |
| Modals, drawers, side panels | 250-400ms |
- Exits run 30-50% faster than entrances. The user has already decided to leave; do not make them wait. Modal enters in 400ms and exits in 200ms.
- Larger elements and longer travel distances get more time. A full-height drawer needs more than a tooltip.
- 400ms is the ceiling for any product UI animation. If motion feels slow, reduce the duration before changing anything else.
The same enter and exit transition at three durations:
Easing
Three curves cover Meteor interfaces:
| Role | Curve | Use for |
|---|---|---|
| Decelerate | cubic-bezier(0.05, 0.7, 0.1, 1) | Elements entering the screen |
| Accelerate | cubic-bezier(0.3, 0, 0.8, 0.15) | Elements leaving the screen |
| Standard | cubic-bezier(0.4, 0, 0.2, 1) | Movement and resizing of elements already on screen |
cubic-bezier(0.05, 0.7, 0.1, 1)cubic-bezier(0.4, 0, 0.2, 1)cubic-bezier(0.3, 0, 0.8, 0.15)linearEntrances decelerate: they start fast, so the interface reads as responding instantly, then land softly. Exits accelerate: they get out of the way without ceremony. The built-in ease keyword is acceptable for hover and color changes.
Reserve linear for constant motion such as the Loader spinner or a Progress Bar. Everywhere else it feels rigid and mechanical. Never use a pure ease-in curve for an entrance; it delays feedback at the exact moment the user is watching most closely.
Entering and exiting
Elements never appear from nothing or grow from a point. Pair a quick fade with a subtle scale from 0.9, so the element always has a visible shape:
The canonical pattern for a Vue transition, including the reduced motion fallback:
.panel-enter-active {
transition: opacity 250ms cubic-bezier(0.05, 0.7, 0.1, 1);
@media (prefers-reduced-motion: no-preference) {
transition:
opacity 250ms cubic-bezier(0.05, 0.7, 0.1, 1),
scale 250ms cubic-bezier(0.05, 0.7, 0.1, 1);
}
}
.panel-leave-active {
transition: opacity 150ms cubic-bezier(0.3, 0, 0.8, 0.15);
@media (prefers-reduced-motion: no-preference) {
transition:
opacity 150ms cubic-bezier(0.3, 0, 0.8, 0.15),
scale 150ms cubic-bezier(0.3, 0, 0.8, 0.15);
}
}
.panel-enter-from,
.panel-leave-to {
opacity: 0;
@media (prefers-reduced-motion: no-preference) {
scale: 0.9;
}
}
- Elements that move as a unit share timing. A modal and its backdrop, or a popover and its arrow, use identical duration and easing. Mismatched timing reads as broken.
- Anchored elements such as a Popover or Tooltip scale from their trigger via
transform-origin, not from the center. Modals are the exception and stay centered. - Exits are quieter than entrances: a fade with a small scale or nudge, not a dramatic slide across the screen.
Implementation
- Animate only
transform(includingtranslateandscale) andopacity. These run on the GPU and skip layout work. Never animatewidth,height,margin,padding, ortop/left. - Use CSS transitions for anything the user can toggle. Transitions retarget smoothly when interrupted mid-animation; keyframes restart from zero and look broken. Reserve
@keyframesfor loaders and one-shot sequences that always run to completion. - Name transition properties explicitly.
transition: allpicks up properties you never intended to animate and breaks silently when styles change. - Do not animate elements into place on initial page load. Save motion for state changes the user causes.
- Animate
transformandopacitywith an explicit property list. - Use the decelerate curve for entrances and the accelerate curve for exits.
- Keep every product UI animation at 400ms or less, with exits faster than entrances.
- Give paired elements, like a modal and its backdrop, identical duration and easing.
- Write
transition: allor inherit timing from a copied snippet. - Animate
width,height,margin, ortop/left. - Animate keyboard-initiated changes such as focus movement.
- Enter from
scale(0)or slide in from outside the viewport.
Accessibility
Respect prefers-reduced-motion in every animation you ship. Reduced motion means removing movement, not removing feedback: drop scale, translate, and other positional changes, and keep quick opacity or color fades that aid comprehension. The pattern in the code sample above gates all movement behind prefers-reduced-motion: no-preference, which is the standard used across Meteor components.
Motion must never delay information. Move focus and trigger screen reader announcements when the state change starts, not after the animation finishes. Errors, confirmations, and focus indicators appear immediately.
Avoid flashing, rapid oscillation, and large sweeping movements across the viewport. They can cause discomfort and motion sickness regardless of the user's system settings.