Skip to content

Motion Design Principles

Our motion system creates a sophisticated, cohesive experience that enhances usability while delighting users. Every animation serves a purpose, guiding attention and providing feedback.

Animation Philosophy

Core Principles

1. Purposeful Motion

Every animation must serve a clear purpose: - Guide Attention: Direct focus to important elements - Provide Feedback: Confirm user actions - Reveal Relationships: Show how elements are connected - Enhance Personality: Express brand character

2. Natural Movement

Animations should feel organic and physics-based: - Use spring physics for playful interactions - Apply exponential easing for smooth transitions - Respect object weight and momentum - Create believable motion paths

3. Performance First

Smooth performance is non-negotiable: - Target 60fps for all animations - Use CSS transforms and opacity - Leverage GPU acceleration - Optimize for mobile devices

4. Accessible by Default

Respect user preferences and needs: - Honor prefers-reduced-motion - Provide motion alternatives - Ensure functionality without animation - Keep animations subtle and non-distracting

Timing Guidelines

Duration Scale

/* Our duration tokens */
--motion-duration-instant: 50ms;    /* Immediate feedback */
--motion-duration-fast: 150ms;      /* Micro-interactions */
--motion-duration-normal: 250ms;    /* Standard transitions */
--motion-duration-slow: 350ms;      /* Complex animations */
--motion-duration-slower: 500ms;    /* Page transitions */
--motion-duration-slowest: 700ms;   /* Elaborate sequences */

When to Use Each Duration

Instant (50ms)

  • Active/pressed states
  • Immediate visual feedback
  • Tooltip appearances

Fast (150ms)

  • Hover states
  • Small UI element transitions
  • Icon animations

Normal (250ms)

  • Most UI transitions
  • Fade in/out effects
  • Standard reveals

Slow (350ms)

  • Modal appearances
  • Complex state changes
  • Accordion expansions

Slower (500ms)

  • Page transitions
  • Large content reveals
  • Stagger animation sequences

Slowest (700ms)

  • Loading animations
  • Complex choreographed sequences
  • Initial page load animations

Easing Guidelines

Our Easing Curves

Ease Out Expo - Primary Easing

cubic-bezier(0.19, 1, 0.22, 1)
Use for most enter animations. Creates smooth deceleration.

Ease In Expo - Exit Animations

cubic-bezier(0.95, 0.05, 0.795, 0.035)
Use for elements leaving the screen. Quick acceleration.

Spring Physics - Playful Motion

cubic-bezier(0.175, 0.885, 0.32, 1.275)
Use for delightful micro-interactions. Slight overshoot.

Ease In Out Quart - Smooth Transitions

cubic-bezier(0.77, 0, 0.175, 1)
Use for continuous motion like scrolling or dragging.

Motion Choreography

Stagger Patterns

Sequential Reveal

Elements appear one after another:

.stagger-item:nth-child(1) { animation-delay: 0ms; }
.stagger-item:nth-child(2) { animation-delay: 50ms; }
.stagger-item:nth-child(3) { animation-delay: 100ms; }

Diagonal Grid

Create visual flow in grid layouts:

// Top-left to bottom-right
.grid-item:nth-child(1) { animation-delay: 0ms; }
.grid-item:nth-child(2) { animation-delay: 50ms; }
.grid-item:nth-child(4) { animation-delay: 50ms; }
.grid-item:nth-child(3) { animation-delay: 100ms; }

Center Outward

Radiate from a focal point:

// Calculate delay based on distance from center
const delay = distanceFromCenter * 25;

Transition Patterns

Crossfade

Smooth content replacement:

.crossfade-exit {
  animation: fadeOut 250ms ease-in;
}
.crossfade-enter {
  animation: fadeIn 250ms ease-out;
}

Shared Element

Maintain visual continuity:

// Track element position
const startPos = element.getBoundingClientRect();
// Animate to new position
element.animate([
  { transform: `translate(${startX}px, ${startY}px)` },
  { transform: 'translate(0, 0)' }
], { duration: 300, easing: 'ease-out' });

Morphing

Transform between states:

.morph {
  transition: all 350ms var(--motion-ease-out-expo);
  transition-property: transform, border-radius, background;
}

Performance Considerations

Optimization Techniques

Use Transform & Opacity

These properties are GPU-accelerated:

/* Good - Hardware accelerated */
.animate {
  transform: translateY(20px);
  opacity: 0;
}

/* Avoid - Causes reflow */
.animate {
  top: 20px;
  display: none;
}

Will-Change Property

Hint browser optimizations:

.will-animate {
  will-change: transform, opacity;
}
/* Remove after animation */
.animated {
  will-change: auto;
}

Contain Layout

Isolate animation impact:

.animation-container {
  contain: layout style;
}

Mobile Optimization

Reduce Complexity

Simplify animations on mobile:

@media (max-width: 768px) {
  .complex-animation {
    animation: simpleFade 200ms ease-out;
  }
}

Touch Feedback

Immediate response to touch:

.touch-target:active {
  transform: scale(0.95);
  transition: transform 50ms ease-out;
}

Implementation Examples

Hero Animation Sequence

// Choreographed hero entrance
class HeroAnimator {
  animate() {
    const timeline = [
      { element: '.hero-title', animation: 'fadeInDown', delay: 0 },
      { element: '.hero-subtitle', animation: 'fadeInUp', delay: 100 },
      { element: '.hero-cta', animation: 'scaleIn', delay: 200 },
      { element: '.hero-image', animation: 'fadeIn', delay: 300 }
    ];

    timeline.forEach(({ element, animation, delay }) => {
      setTimeout(() => {
        document.querySelector(element)?.classList.add(animation);
      }, delay);
    });
  }
}

Scroll-Triggered Animation

// Reveal elements on scroll
const observerOptions = {
  threshold: 0.1,
  rootMargin: '0px 0px -100px 0px'
};

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('in-view');
    }
  });
}, observerOptions);

document.querySelectorAll('.scroll-animate').forEach(el => {
  observer.observe(el);
});

Micro-Interaction Example

/* Button with spring physics */
.button {
  transition: all 150ms var(--motion-spring-gentle);
}

.button:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
}

.button:active {
  transform: translateY(0);
  transition-duration: 50ms;
}

Page Transition

// Smooth page transitions
class PageTransition {
  async navigate(url) {
    // Exit animation
    document.body.classList.add('page-exit');
    await this.wait(250);

    // Load new content
    const content = await this.fetchContent(url);
    this.updateDOM(content);

    // Enter animation
    document.body.classList.remove('page-exit');
    document.body.classList.add('page-enter');
  }
}

Accessibility Guidelines

Reduced Motion Support

/* Respect user preferences */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }

  /* Provide alternative feedback */
  .animated-element {
    opacity: 1;
    transform: none;
  }
}

Focus Indicators

/* Animated focus states */
.focus-visible:focus {
  outline: none;
  animation: focusRing 200ms ease-out;
}

@keyframes focusRing {
  from {
    box-shadow: 0 0 0 0 var(--color-primary);
  }
  to {
    box-shadow: 0 0 0 4px var(--color-primary-alpha);
  }
}

Testing & Debugging

Performance Monitoring

// Monitor animation performance
const perfObserver = new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    if (entry.duration > 16.67) { // Below 60fps
      console.warn('Animation frame dropped:', entry);
    }
  });
});

perfObserver.observe({ entryTypes: ['measure'] });

Debug Mode

/* Slow down animations for debugging */
.debug-motion * {
  animation-duration: 3s !important;
  transition-duration: 3s !important;
}

Motion System Checklist

  • Every animation has a clear purpose
  • Timing feels natural and appropriate
  • Performance is consistently 60fps
  • Reduced motion preferences are respected
  • Animations enhance rather than distract
  • Touch interactions feel responsive
  • Loading states are smooth and informative
  • Page transitions maintain context
  • Focus states are clearly visible
  • Motion creates a cohesive experience

Remember: Great motion design is felt, not seen. When done right, users will feel your interface is fast, smooth, and delightful without consciously noticing the animations.