12 Motion Design Principles for the Modern Web
Animation on the web has matured. Here are the principles that separate purposeful motion from gratuitous noise — and how to implement them.
Motion design on the web has come a long way. We've moved past the jQuery.slideDown() era into a world where Framer Motion, GSAP, and CSS animations give us near-cinematic control over every pixel. But with great power comes the responsibility not to make your users feel seasick.
Here are 12 principles that guide every motion decision we make at Relogic.
1. Motion should communicate, not decorate
Every animation should earn its place by conveying information. A loading spinner communicates progress. A card flipping reveals additional content. A shake communicates an error. If you can't articulate what an animation communicates, remove it.
// Bad: Animation for animation's sake
const decorative = {
animate: { rotate: [0, 5, -5, 0] }, // What does this say?
};
// Good: Shake communicates validation error
const errorShake = {
animate: { x: [0, -8, 8, -8, 8, 0] },
transition: { duration: 0.4, ease: "easeInOut" },
};2. Use easing intentionally
Linear motion feels mechanical and unnatural. Ease-in feels like something is accelerating away. Ease-out feels like deceleration — natural, finished. Custom cubic-bezier curves let you define exactly the "personality" of your motion.
// Standard eases and when to use them
const eases = {
// Elements entering — start fast, slow to rest
enter: [0.25, 0.46, 0.45, 0.94],
// Elements exiting — slow start, fast exit
exit: [0.55, 0, 1, 0.45],
// Dramatic reveals — slow, then explosive
expo: [0.76, 0, 0.24, 1],
// Bouncy, playful interactions
spring: { type: "spring", stiffness: 300, damping: 20 },
};3. Respect reduced motion preferences
This is non-negotiable. Some users experience vestibular disorders that make certain animations physically uncomfortable or disabling.
import { useReducedMotion } from "framer-motion";
function AnimatedCard() {
const prefersReduced = useReducedMotion();
return (
<motion.div
initial={{ opacity: 0, y: prefersReduced ? 0 : 40 }}
animate={{ opacity: 1, y: 0 }}
>
{/* content */}
</motion.div>
);
}4. Duration is context-dependent
Small elements (icons, tooltips) → 100–200ms. Medium elements (cards, panels) → 200–400ms. Large elements (full-page transitions, hero animations) → 400–800ms. Breaking these ranges makes UI feel sluggish or jarring.
5. Stagger creates hierarchy
When multiple elements animate simultaneously, they feel like a blob. Staggering entrance animations creates a reading sequence that guides the eye.
const stagger = {
animate: {
transition: {
staggerChildren: 0.08,
delayChildren: 0.1,
}
}
};6. Use layout animations for state changes
When an element changes size or position, a layout animation prevents the jump-cut that makes UI feel cheap.
// Framer Motion handles the interpolation automatically
<motion.div layout>
{isExpanded && <ExpandedContent />}
</motion.div>7. Parallax should be subtle
A 10–15% depth difference between layers reads as parallax. More than that and users start noticing the mechanism over the experience. GSAP ScrollTrigger with scrub: true gives you the smoothest implementation.
8. Loading states deserve animation
Skeleton screens with subtle pulse animations dramatically reduce perceived loading time. The brain is busy processing the animation and less focused on the wait.
9. Scroll-triggered vs. intersection-triggered
GSAP ScrollTrigger is best for continuous scroll-scrubbing effects. Framer Motion's useInView is better for one-shot entrance animations when elements enter the viewport. Use the right tool.
10. Animate properties that GPU-accelerates
transform and opacity are composited — they don't trigger layout or paint. Everything else (width, height, top, left, margin, etc.) does. This is the difference between 60fps and 12fps.
11. Spring physics feel more alive
Spring animations behave like objects with mass and tension. They overshoot slightly and settle naturally — like real objects. This gives UI a tactile quality that is hard to explain but immediately felt.
const springConfig = {
type: "spring",
stiffness: 200, // Higher = faster/tighter
damping: 20, // Higher = less bounce
mass: 0.8, // Higher = heavier, slower to start
};12. Test on low-end devices
Your 2024 MacBook Pro handles everything beautifully. The median user is on a mid-range Android with 60fps throttled by the browser. Always performance-test animations on constrained hardware before shipping.
Motion design is ultimately about empathy — understanding how your users perceive change over time, and crafting that change to be clear, purposeful, and delightful. Get these principles right and your animations stop being decorations and start being UX.
Relogic Studio
Web development agency specialising in motion-driven digital experiences.