Motion & Behavior
Purposeful animation that communicates, not decorates. Every transition, every easing curve, every duration serves a function.
Motion Philosophy
Apple's approach to motion is grounded in three core principles. Animation should never exist just to impress — it should communicate, orient, and delight in service of the user's goals.
Purposeful
Motion communicates state changes, confirms actions, and guides attention. Never decorative. Every animation has a reason to exist.
Physical
Animations follow physical laws — spring physics, inertia, momentum. Objects feel like they have weight and respond naturally to interaction.
Respectful
Always honor prefers-reduced-motion. Users with vestibular disorders or motion sensitivity must always be considered.
Easing Functions
Easing curves define how an animation accelerates and decelerates. The right curve makes motion feel natural; the wrong one feels mechanical or jarring.
Hover each row to see the animation play
Duration Reference
Consistency in timing makes an interface feel cohesive. Use the right duration token for every interaction type.
Transition Types
Five core transition patterns cover virtually every UI motion need. Click each "Play" button to see the animation in action.
Fade
opacity 0 → 1
200ms ease
Scale
scale 0.95 → 1 + fade
250ms spring
Slide Up
translateY(12px) → 0 + fade
300ms decelerate
Slide In
translateX(-20px) → 0 + fade
300ms decelerate
Spring Scale
scale 0.8 → 1.05 → 1
spring easing
Functional Motion Examples
These real-world animation patterns solve specific UX problems. Each exists to communicate something — not to impress.
Loading → Content
Skeleton screens reduce perceived load time and prevent layout shift.
The new motion system looks incredibly polished. Great work on the spring easing!
Success Confirmation
A spring-animated checkmark confirms the action completed successfully.
Form Validation
A brief shake communicates an error without being disruptive or alarming.
Expand / Collapse
Directional arrow rotation signals the state before the content animates open.
Reduced Motion
The prefers-reduced-motion media query is non-negotiable. Users with vestibular disorders can experience nausea or disorientation from motion. Always provide a static alternative.
Animation plays normally for users who haven't opted out
Static fallback — shape/color conveys state without motion
/* ✅ Always wrap animations in a media query */
.animated-element {
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@media (prefers-reduced-motion: reduce) {
.animated-element {
/* Remove animation, keep functional state */
transition: none;
}
/* Or use instantaneous transitions */
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
CSS Implementation
Complete utility classes and CSS custom properties for implementing the motion system in any project.
:root {
/* Duration Tokens */
--duration-instant: 100ms;
--duration-fast: 200ms;
--duration-normal: 300ms;
--duration-slow: 500ms;
--duration-slower: 700ms;
/* Easing Tokens */
--easing-linear: linear;
--easing-standard: cubic-bezier(0.2, 0, 0, 1);
--easing-decelerate: cubic-bezier(0, 0, 0.2, 1);
--easing-accelerate: cubic-bezier(0.3, 0, 1, 1);
--easing-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* Utility: Fade in */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
/* Utility: Scale in */
@keyframes scaleIn {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
/* Utility: Slide up */
@keyframes slideUp {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
/* Utility: Slide in from left */
@keyframes slideInLeft {
from { opacity: 0; transform: translateX(-20px); }
to { opacity: 1; transform: translateX(0); }
}
/* Animation utility classes */
.animate-fade-in { animation: fadeIn var(--duration-fast) var(--easing-standard) both; }
.animate-scale-in { animation: scaleIn var(--duration-normal) var(--easing-spring) both; }
.animate-slide-up { animation: slideUp var(--duration-normal) var(--easing-decelerate) both; }
.animate-slide-left { animation: slideInLeft var(--duration-normal) var(--easing-decelerate) both; }
/* Stagger children with delay */
.stagger > *:nth-child(1) { animation-delay: 0ms; }
.stagger > *:nth-child(2) { animation-delay: 50ms; }
.stagger > *:nth-child(3) { animation-delay: 100ms; }
.stagger > *:nth-child(4) { animation-delay: 150ms; }
/* Respect reduced motion */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}