Last updated: Aug 4, 2025, 11:26 AM UTC

Motion Design Guidelines

Status: Complete Animation System
Version: 1.0.0
Verified: Performance optimized


Executive Summary

Motion brings interfaces to lifeβ€”thoughtful animation guides attention, provides feedback, and creates delightful experiences. Our motion system balances personality with performance, ensuring every animation has purpose while maintaining 60fps smoothness.

Motion Philosophy

Principle Implementation User Impact
Purposeful Every animation has a job Clearer understanding
Performant 60fps on all devices Smooth experience
Natural Physics-based easing Feels intuitive
Accessible Respects preferences Inclusive for all
Subtle Enhance, don't distract Focus on content

Motion Architecture

graph TD A[Motion System] --> B[Micro-interactions] A --> C[Transitions] A --> D[Loading States] A --> E[Gestures] B --> F[Hover, Click, Focus] C --> G[Page, Modal, Tab] D --> H[Skeleton, Progress] E --> I[Swipe, Drag, Pinch] style A fill:#2563EB,color:#fff style B fill:#e1f5fe style C fill:#f3e5f5 style D fill:#e8f5e8 style E fill:#fff3e0

Section 1: Timing & Easing (800 words)

Duration Scale

Our timing system creates consistent, predictable animations that feel natural and responsive.

Animation duration scale showing timing values from 50ms micro-interactions to 700ms page transitions

Systematic timing scale for consistent animation feel

Duration Tokens

:root {
  /* Duration scale */
  --duration-instant: 50ms;    /* Immediate feedback */
  --duration-micro: 100ms;     /* Tiny interactions */
  --duration-fast: 150ms;      /* Quick responses */
  --duration-normal: 250ms;    /* Standard transitions */
  --duration-moderate: 350ms;  /* Complex animations */
  --duration-slow: 500ms;      /* Page transitions */
  --duration-slower: 700ms;    /* Elaborate effects */
  --duration-slowest: 1000ms;  /* Special occasions */
}

Duration Guidelines:

Animation Type Duration Use Cases
Hover states 100-150ms Buttons, links, cards
Focus states 150ms Form inputs, buttons
Toggles 200ms Switches, checkboxes
Dropdowns 250ms Menus, selects
Modals 300ms Dialogs, overlays
Page transitions 350-500ms Route changes
Loading Continuous Progress indicators

Easing Functions

:root {
  /* Natural easing curves */
  --ease-linear: linear;
  --ease-in: cubic-bezier(0.4, 0, 1, 1);
  --ease-out: cubic-bezier(0, 0, 0.2, 1);
  --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
  
  /* Expressive curves */
  --ease-spring: cubic-bezier(0.5, 1.5, 0.5, 1);
  --ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
  --ease-elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6);
  
  /* Entrance/exit curves */
  --ease-enter: var(--ease-out);
  --ease-exit: var(--ease-in);
}

Easing Selection Guide:

// Choose easing based on animation purpose
const easingGuide = {
  // Objects entering view
  enter: 'ease-out',      // Decelerate into place
  
  // Objects leaving view
  exit: 'ease-in',        // Accelerate away
  
  // State changes
  transition: 'ease-in-out', // Smooth both ways
  
  // Playful feedback
  bounce: 'ease-bounce',   // Overshoot effect
  
  // Continuous motion
  loop: 'linear'          // Constant speed
};

Motion Curves Visualized

/* Real-world examples */
.button {
  transition: all var(--duration-fast) var(--ease-out);
  
  &:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  }
  
  &:active {
    transition-duration: var(--duration-micro);
    transform: translateY(0);
  }
}

.modal {
  animation: modalEnter var(--duration-moderate) var(--ease-spring);
}

@keyframes modalEnter {
  from {
    opacity: 0;
    transform: scale(0.9) translateY(20px);
  }
  to {
    opacity: 1;
    transform: scale(1) translateY(0);
  }
}

Section 2: Micro-interactions (700 words)

Interactive Feedback

Micro-interactions provide immediate feedback for user actions, making interfaces feel responsive and alive.

Micro-interaction examples showing hover states, clicks, focus effects, and state changes

Subtle micro-interactions enhancing user feedback

Hover Interactions

/* Button hover elevation */
.interactive-element {
  transition: transform var(--duration-fast) var(--ease-out),
              box-shadow var(--duration-fast) var(--ease-out);
  
  &:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  }
}

/* Link underline animation */
.link {
  position: relative;
  
  &::after {
    content: '';
    position: absolute;
    bottom: -2px;
    left: 0;
    width: 0;
    height: 2px;
    background: currentColor;
    transition: width var(--duration-normal) var(--ease-out);
  }
  
  &:hover::after {
    width: 100%;
  }
}

Click Feedback

/* Ripple effect */
.ripple {
  position: relative;
  overflow: hidden;
  
  &::before {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 0;
    height: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.5);
    transform: translate(-50%, -50%);
    transition: width var(--duration-moderate) var(--ease-out),
                height var(--duration-moderate) var(--ease-out);
  }
  
  &:active::before {
    width: 100px;
    height: 100px;
  }
}

/* Press feedback */
.pressable {
  transition: transform var(--duration-micro) var(--ease-out);
  
  &:active {
    transform: scale(0.98);
  }
}

State Changes

/* Toggle switch */
.switch {
  .slider {
    transition: transform var(--duration-fast) var(--ease-spring);
    transform: translateX(0);
  }
  
  &.is-on .slider {
    transform: translateX(20px);
  }
}

/* Checkbox animation */
.checkbox {
  .checkmark {
    stroke-dasharray: 30;
    stroke-dashoffset: 30;
    transition: stroke-dashoffset var(--duration-normal) var(--ease-out);
  }
  
  input:checked ~ .checkmark {
    stroke-dashoffset: 0;
  }
}

Section 3: Page Transitions (700 words)

Navigation Animations

Smooth transitions between pages and sections maintain context and orientation.

Page transition examples showing fade, slide, and morph transitions between different views

Contextual page transitions maintaining spatial relationships

Route Transitions

/* Fade transition */
.page-fade-enter {
  opacity: 0;
}

.page-fade-enter-active {
  opacity: 1;
  transition: opacity var(--duration-moderate) var(--ease-out);
}

.page-fade-exit {
  opacity: 1;
}

.page-fade-exit-active {
  opacity: 0;
  transition: opacity var(--duration-fast) var(--ease-in);
}

/* Slide transition */
.page-slide-enter {
  transform: translateX(100%);
}

.page-slide-enter-active {
  transform: translateX(0);
  transition: transform var(--duration-slow) var(--ease-out);
}

.page-slide-exit-active {
  transform: translateX(-20%);
  opacity: 0;
  transition: all var(--duration-slow) var(--ease-in);
}

πŸͺŸ Modal Animations

/* Modal entrance */
@keyframes modalEnter {
  from {
    opacity: 0;
    transform: scale(0.95) translateY(10px);
  }
  to {
    opacity: 1;
    transform: scale(1) translateY(0);
  }
}

/* Backdrop fade */
@keyframes backdropFade {
  from { opacity: 0; }
  to { opacity: 1; }
}

.modal {
  animation: modalEnter var(--duration-moderate) var(--ease-spring);
}

.modal-backdrop {
  animation: backdropFade var(--duration-fast) var(--ease-out);
}

Tab Transitions

// Tab content transitions
const tabTransition = {
  '.tab-panel': {
    position: 'relative',
    
    '&.entering': {
      animation: 'tabEnter 300ms ease-out'
    },
    
    '&.exiting': {
      animation: 'tabExit 200ms ease-in',
      position: 'absolute',
      top: 0,
      left: 0,
      right: 0
    }
  }
};

// Directional awareness
const getTabAnimation = (oldIndex, newIndex) => {
  return oldIndex < newIndex ? 'slideLeft' : 'slideRight';
};

Section 4: Loading & Progress (600 words)

Loading States

Loading animations maintain engagement during wait times while setting accurate expectations.

Loading patterns including skeleton screens, progress bars, and spinners

Progressive loading patterns for different contexts

Skeleton Screens

/* Skeleton pulse animation */
@keyframes skeletonPulse {
  0% {
    background-position: -200% 0;
  }
  100% {
    background-position: 200% 0;
  }
}

.skeleton {
  background: linear-gradient(
    90deg,
    var(--gray-200) 25%,
    var(--gray-100) 50%,
    var(--gray-200) 75%
  );
  background-size: 200% 100%;
  animation: skeletonPulse 1.5s ease-in-out infinite;
}

Progress Indicators

/* Determinate progress */
.progress-bar {
  .fill {
    transition: width var(--duration-moderate) var(--ease-out);
    transform-origin: left;
  }
}

/* Indeterminate progress */
.progress-indeterminate {
  .fill {
    animation: progressSlide 1.5s ease-in-out infinite;
  }
}

@keyframes progressSlide {
  0% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(200%);
  }
}

/* Circular spinner */
.spinner {
  animation: rotate 1s linear infinite;
  
  .circle {
    stroke-dasharray: 150;
    stroke-dashoffset: 0;
    animation: dash 1.5s ease-in-out infinite;
  }
}

@keyframes dash {
  0% {
    stroke-dashoffset: 150;
  }
  50% {
    stroke-dashoffset: 45;
  }
  100% {
    stroke-dashoffset: 150;
  }
}

Section 5: Gesture Animations (500 words)

Touch Interactions

Gesture-based animations provide natural feedback for touch interfaces.

Gesture animations showing swipe, pinch, drag, and pull-to-refresh interactions

Natural gesture responses for touch interfaces

Swipe Actions

// Swipe to delete
const swipeAnimation = {
  threshold: 100, // pixels
  
  onSwipe: (distance) => {
    const progress = Math.min(distance / threshold, 1);
    element.style.transform = `translateX(${distance}px)`;
    element.style.opacity = 1 - progress * 0.5;
  },
  
  onRelease: (distance) => {
    if (distance > threshold) {
      // Complete action
      element.style.transition = 'all 200ms ease-out';
      element.style.transform = 'translateX(100%)';
      element.style.opacity = '0';
    } else {
      // Snap back
      element.style.transition = 'all 200ms ease-out';
      element.style.transform = 'translateX(0)';
      element.style.opacity = '1';
    }
  }
};

Pull to Refresh

.pull-to-refresh {
  .indicator {
    transform: translateY(-100%);
    transition: transform var(--duration-normal) var(--ease-out);
    
    &.pulling {
      transform: translateY(0);
      transition: none;
    }
    
    &.refreshing {
      animation: spin 1s linear infinite;
    }
  }
}

Section 6: Accessibility & Performance (400 words)

Motion Accessibility

Respecting user preferences ensures animations enhance rather than hinder the experience.

Accessibility

  • Respect prefers-reduced-motion
  • Provide pause controls
  • Avoid flashing
  • Ensure readability during motion
  • Test with screen readers

Performance

  • Use transform & opacity only
  • Enable GPU acceleration
  • Batch animations
  • Test on low-end devices
  • Monitor frame rates

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;
  }
  
  /* Keep essential motion */
  .spinner {
    animation-duration: 1s !important;
  }
}

Performance Optimization

/* GPU acceleration */
.animated {
  will-change: transform;
  transform: translateZ(0);
}

/* Clean up after animation */
.animation-complete {
  will-change: auto;
}

Conclusion

Motion is the soul of interaction. Our comprehensive motion system creates interfaces that feel alive and responsive while maintaining performance and accessibility. Every animation serves a purpose, guiding users naturally through their journey.

Next Steps

  1. Review Component Library for motion in practice
  2. Explore Design Tokens for animation variables
  3. Test with Performance Tools

This motion system ensures NudgeCampaign feels delightful and responsive while respecting user preferences and device capabilities.