← All motion systems
Motion System

Apple Human Interface Guidelines

Physics-based, spring-driven motion that makes digital interfaces feel like they inhabit the real world.

get_motion("apple")
Browse all

Motion System: Apple Human Interface Guidelines

Category: Platform / Design System (iOS, macOS, iPadOS, watchOS) Physics-based, spring-driven motion that makes digital interfaces feel like they inhabit the real world.

Motion Philosophy

Apple's approach to motion is grounded in a single conviction: interfaces should feel physical. When a sheet slides up from the bottom of the screen, it should feel like something you could grab. When a popover appears, it should emerge from its source element as if connected to it. When you swipe away a notification, it should follow your finger with inertia and settle naturally. This is why spring physics are the default in UIKit and SwiftUI, not an enhancement — they are the foundational model for how Apple thinks about interactive motion.

This physicality serves a purpose beyond aesthetics. Motion in Apple's platform communicates spatial relationships. The cross-fade transition of a modal says "this is a separate context." The push transition of a navigation controller says "you went deeper." The zoom transition of a full-screen photo says "this object expanded into this space." These spatial metaphors reduce cognitive load: users understand where they came from and how to get back without having to think explicitly about state. The consistency of these transitions across every Apple app — system and third-party alike — is what allows users to build reliable spatial mental models over time.

Where IBM avoids springs because they are unpredictable, Apple embraces springs precisely because they are responsive — their behavior scales naturally with gesture velocity. Flick something fast and it moves fast. Drag something slowly and it settles slowly. This coupling of motion to input is what makes iOS feel alive and direct. Critically, Apple's spring parameters are carefully tuned to avoid perceptible bounce in standard UI elements: the damping ratio is high enough that elements settle on first approach, with no oscillation unless specifically intended (as in a game or Lottie illustration).

Duration Scale

TokenValueUse
instant0msVoiceOver state changes, direct manipulation following a finger
fast150msTooltip appearance, badge updates, small icon transitions
default300msStandard push/pop navigation, modal presentation, sheet dismiss
slow400msFull-screen transitions, app launch splash fade, split view resize
slower500msHero image zoom transitions, onboarding sequences

Note: UIKit and SwiftUI spring durations are emergent — they are determined by stiffness, damping, and mass rather than set explicitly. The values above describe perceived duration, not a fixed parameter. UIView.animate(withDuration:) uses seconds; SwiftUI .animation(.spring(...)) uses response/dampingFraction.

Easing

TokenCurveUse
ease-outcubic-bezier(0.33, 1, 0.68, 1)Non-spring elements entering — system alerts, static overlays
ease-incubic-bezier(0.32, 0, 0.67, 0)Elements exiting — dismissing UI that the user is leaving behind
ease-in-outcubic-bezier(0.65, 0, 0.35, 1)Repositioning, crossfades between states of the same element
decelerationcubic-bezier(0.0, 0.0, 0.2, 1.0)Elements entering from off-screen — strong deceleration matches physical throw
linearcubic-bezier(0, 0, 1, 1)Progress bars, activity indicators, continuous spinners

Apple's preferred curve for entering elements is strong deceleration — the element arrives with high velocity and brakes firmly, communicating that it has traveled from somewhere real.

Spring Configs (Framer Motion / react-spring)

Apple's UIKit springs are specified by dampingRatio and initialSpringVelocity. SwiftUI uses response (approximate duration), dampingFraction, and blendDuration. These translate to Framer Motion / react-spring approximately as follows:

  • Default (UIKit standard, modal sheet, navigation push): stiffness: 300, damping: 30, mass: 1 — settles without bounce, ~300ms perceived
  • Snappy (interactive dismiss, swipe-to-go-back): stiffness: 500, damping: 40, mass: 1 — fast response, matches gesture velocity
  • Gentle (large element repositioning, split-view animation): stiffness: 170, damping: 26, mass: 1 — softer arrival, slight but controlled overshoot
  • Tight (button press feedback, haptic-paired interactions): stiffness: 700, damping: 60, mass: 1 — near-instant with no perceptible spring tail

UIKit reference: UISpringTimingParameters(dampingRatio: 0.7, initialSpringVelocity: 0.5) for default transitions. SwiftUI: .spring(response: 0.35, dampingFraction: 0.7).

Stagger Patterns

  • List items (UITableView / List): No explicit stagger in system components — rows fade in simultaneously during scroll. Custom stagger: 25ms between items, max 8 items.
  • Cards in grid (UICollectionView / LazyVGrid): 30ms between each card when loading, max 6 cards; beyond that, load simultaneously.
  • Nav items (tab bar, sidebar): No stagger — tab bars and sidebars appear as a unit.
  • App icons (Springboard, widget gallery): 20ms between each icon, staggered in a wave from the interaction origin point.

Enter / Exit Patterns

Navigation Push (UINavigationController)

enter: incoming view translates from +100% x to 0, title + back button fade in; outgoing view translates to -30% x, duration: default (300ms), spring: Default
exit (pop): reverse — incoming translates from -30% to 0, outgoing from 0 to +100%, matched to gesture velocity if interactive

Modal Sheet (UISheetPresentationController / .sheet)

enter: translateY 100%→0, corner radius animates to match sheet radius, duration: default (300ms), spring: Default
exit: translateY 0→100%, duration: default (300ms), spring: Snappy

Fade + Slide (alerts, non-interactive overlays)

enter: opacity 0→1, scale 0.94→1, duration: default (300ms), ease: ease-out
exit: opacity 1→0, scale 1→0.94, duration: fast (150ms), ease: ease-in

Scale Pop (popovers, context menus, tooltips)

enter: opacity 0→1, scale 0.8→1, origin: anchor point (source element), duration: fast (200ms), spring: Snappy
exit: opacity 1→0, scale 1→0.8, duration: fast (150ms), ease: ease-in

Hero / Magic Move (shared element transition)

The source element's frame animates directly to the destination element's frame — no fade, no separate enter/exit. The element "becomes" the destination view. Duration: slow (400ms), spring: Gentle. Used in Photos, App Store, Safari Reader.

Contextual Menu (UIContextMenuInteraction)

enter: source element scales down to ~0.95 (pressed feel), background blurs in over 200ms. Menu items appear from anchor point: scale 0.8→1, opacity 0→1, spring: Snappy, staggered 20ms.

Interaction States

  • Hover (iPadOS, macOS): Subtle highlight appears over 150ms ease-out. System uses UIHoverGestureRecognizer; no scale on most controls. List rows show .systemFill tint on hover.
  • Press/Active: Controls scale to 0.95 immediately on touchDown (0ms delay, direct manipulation feel). On buttons: background dims over 0ms. Haptic feedback (UIImpactFeedbackGenerator, .light) fires simultaneously with visual. Release returns to 1.0 via Tight spring (stiffness: 700, damping: 60).
  • Focus (keyboard, Apple TV, accessibility): Focus ring appears with 0ms delay. On tvOS, the focused element lifts with a subtle scale (1→1.08) and shadow deepens over 200ms Default spring — the "parallax" hover effect. On iOS with keyboard, the system focus ring is a static blue outline, no animation.
  • Loading: UIActivityIndicatorView uses a 12-segment rotary spinner at 0.9s per revolution, linear. Skeleton views use a shimmer that travels left to right over 1500ms, ease-in-out, looping with a 400ms gap between passes. SwiftUI .redacted(reason: .placeholder) shows a static frosted placeholder with no animation by default.
  • Reduced motion: When UIAccessibility.isReduceMotionEnabled, sliding transitions become crossfades (opacity only). Parallax effects on home screen are removed. Springs are replaced with 250ms ease-out fades. No scale transforms. This is enforced at the UIKit level and should be respected in all custom animations via UIAccessibility.isReduceMotionEnabled checks.

Rules

  • Use spring physics for everything interactive. If a user can touch, drag, swipe, or tap it, the response should be a spring, not a cubic-bezier. Reserve cubic-bezier for non-interactive or ambient animations.
  • Respect spatial metaphors consistently. Push transitions move left/right. Sheets move up/down. Alerts appear in place. Mixing metaphors without reason breaks the user's spatial model and creates disorientation.
  • Match animation to gesture velocity when supporting interactive dismissal. A slow drag should produce a slow settle; a fast flick should produce a fast exit. UIViewPropertyAnimator with continueAnimation(withTimingParameters:durationFactor:) enables this.
  • Always check UIAccessibility.isReduceMotionEnabled before running any transform-based animation. Provide an opacity-only or static fallback. This is required for App Store submission compliance.
  • Do not animate more than one full-screen transition at a time. If a notification arrives while a modal is animating, queue the notification display until the transition completes.

Use this in your agent

The DesignMD MCP server returns this full motion system in one call. Combine it with design tokens using get_full_system.

get_full_system("apple")
Set up MCP →