← All motion systems
Motion System

Figma

Direct manipulation as the motion system — the cursor is the animation.

get_motion("figma")
Browse all

Motion System: Figma

Category: Design Tool Direct manipulation as the motion system — the cursor is the animation.

Motion Philosophy

Figma's motion philosophy begins with a fundamental insight: design tools are not applications you use, they are surfaces you inhabit. The user is not navigating to content — they are authoring it. In this context, the traditional model of "UI transitions" breaks down almost entirely. When a designer drags a frame, the frame moves. When they resize a component, it resizes. There is no animation layer mediating between the user's hand and the result; the cursor is the animation. This is the principle of direct manipulation, and in Figma it is applied with unusual rigor.

The motion system is therefore split into two distinct categories that rarely intersect. The first is direct manipulation: anything that tracks the user's cursor or input in real time operates at 0ms with no easing. Frame repositioning, node dragging, handle pulls, canvas pan and zoom — all of these follow the cursor exactly. Introducing easing to cursor-tracked elements would be a fundamental design error; it would make the tool feel like it is lagging behind the designer's intent. The second category is UI chrome: everything that is not the canvas itself. Panels sliding in, modals opening, dropdowns appearing, context menus dismissing. These follow conventional fast-transition principles at 100–200ms.

The interesting tension in Figma's motion system is at the boundary between canvas and chrome. Panel open/close uses spring physics because panels are adjacent to the canvas — the designer is extending the workspace, and the spring communicates the physical expansion of their environment. Resize handles use an elastic feel on release because the designer has been pulling on something, and springs naturally communicate the release of tension. These are the places where Figma's motion becomes expressive: not as decoration, but as physical feedback for physical-feeling interactions.

Duration Scale

TokenValueUse
duration-direct0msAll cursor-tracked direct manipulation — dragging, resizing, canvas pan/zoom
duration-snap50msSnap indicators, alignment guides appearing, smart selection highlights
duration-fast100msIcon state changes, toggle switches, property panel value updates
duration-chrome150msToolbar popovers, color picker open, small UI overlays
duration-panel200msLeft panel open/close (layers, assets), right panel transitions, plugin panels
duration-modal200msExport dialog, share modal, prototype preview
duration-canvas300ms"Fit to screen" zoom, "zoom to selection" — camera moves on the canvas

Easing

TokenCurveUse
ease-outcubic-bezier(0, 0, 0.2, 1)Chrome elements entering — clean deceleration
ease-incubic-bezier(0.4, 0, 1, 1)Chrome elements exiting
ease-in-outcubic-bezier(0.4, 0, 0.2, 1)Canvas camera transitions (zoom to fit, zoom to selection)
ease-panelcubic-bezier(0.16, 1, 0.3, 1)Panel edges entering — aggressive deceleration for the workspace-extension feel
ease-linearcubic-bezier(0, 0, 1, 1)Progress bars, loading bars, shimmer

Direct manipulation uses no easing — frames, nodes, and handles follow the pointer exactly. Any easing applied to cursor-tracked elements would be a bug, not a feature.

Spring Configs (Framer Motion)

Springs are used for panel behaviors and interactive handles, not for general UI chrome.

  • Left/right panel open: stiffness: 320, damping: 30, mass: 1 — slight overshoot, communicates workspace expanding
  • Plugin panel: stiffness: 280, damping: 28, mass: 1 — slightly softer, plugins feel like guest surfaces
  • Resize handle release (elastic snap): stiffness: 600, damping: 22, mass: 0.5 — elastic snap when releasing a constrained resize, brief overshoot, communicates the release of tension
  • Context menu (right-click): stiffness: 500, damping: 40, mass: 0.7 — near-instant, very slight scale overshoot
  • Prototype preview frame enter: stiffness: 240, damping: 28, mass: 1 — gentle bounce as prototype preview window appears

Stagger Patterns

  • Layer panel items (initial load): 6ms between items, capped at 15 items — barely perceptible, prevents simultaneous flash
  • Asset search results: 12ms between each component card
  • Plugin grid in Community: 20ms between each plugin card, left to right, top to bottom
  • Toolbar icon group (on first launch): 30ms between icon groups (not individual icons) — a single graceful settle
  • Export format options: no stagger — appear simultaneously

Stagger is never applied during real-time collaboration events (another user adding frames, cursor presence appearing). Remote events animate in individually and instantly.

Enter / Exit Patterns

Direct Manipulation (canvas elements — the most important pattern)

drag start: 0ms — element follows cursor position with no transition
drag end (release): element snaps to final position at 0ms if no physics trigger. If snapping to a grid or guide: translateX/Y to snap position at duration-snap (50ms), ease: ease-out
resize: follows cursor at 0ms. On constraint release: spring Resize handle release config

Panel Open/Close (layers panel, assets panel, design panel)

enter: translateX -100%→0 (left panels), translateX 100%→0 (right panels), spring: Panel open config
exit: translateX 0→-100% or 0→100%, duration: duration-panel (200ms) × 0.7 = 140ms, ease: ease-in
canvas: simultaneously resizes to fill new available space, duration: same as panel transition

Context Menu (right-click menu)

enter: opacity 0→1, scale 0.97→1 (transform-origin: click point), spring: Context menu config
exit: opacity 1→0, scale 1→0.97, duration: duration-fast (100ms), ease: ease-in

Popover / Color Picker

enter: opacity 0→1, scale 0.98→1, translateY 4px→0, duration: duration-chrome (150ms), ease: ease-out
exit: opacity 1→0, duration: duration-fast (100ms), ease: ease-in

Modal (export, share)

enter: opacity 0→1, scale 0.97→1, duration: duration-modal (200ms), ease: ease-out
backdrop: opacity 0→0.5, duration: duration-modal (200ms), ease: ease-out
exit: opacity 1→0, duration: duration-fast (100ms), ease: ease-in

Canvas Camera (zoom to fit, zoom to selection, cmd+0)

viewBox animates: current bounds → target bounds, duration: duration-canvas (300ms), ease: ease-in-out
simultaneously: zoom level counter in toolbar crossfades to new value at duration-fast (100ms)

Multiplayer cursor (collaborator cursor appearing)

enter: opacity 0→1 with name tag, duration: duration-fast (100ms), ease: ease-out
movement: follows real-time position at 0ms — no interpolation, no easing on collaborator cursors
exit (user leaves or idles): opacity 1→0, duration: duration-chrome (150ms), ease: ease-in

Interaction States

  • Hover (canvas elements): Selection handles appear at duration-snap (50ms) ease-out. Hover outline appears at 0ms — it is a cursor state, not an animation. Component description tooltip appears at duration-chrome (150ms) with a 600ms delay (prevents tooltip spam during canvas exploration).
  • Hover (chrome): Background fill at duration-fast (100ms) ease-out. Icon color transitions at duration-fast (100ms).
  • Press/Active (toolbar): Scale 0.92 on toolbar icon at 80ms ease-out, returns to 1.0 on release via Resize handle spring at low stiffness (200, damping 25).
  • Focus (property inputs): Input border highlights at 0ms — inputs in the right panel must respond at typing speed. No animation on focus.
  • Loading (file open, auto-save): Progress bar at the top of the window, linear easing, width animates from 0 to 100% over estimated load time. On save: brief checkmark icon swap at 0ms, then fade out at duration-fast (100ms).
  • Skeleton (file thumbnails in home screen): Shimmer at 1400ms ease-in-out infinite.
  • Reduced motion: All panel springs snap to end position. Canvas camera transitions become instant (0ms). Multiplayer cursors appear/disappear at 0ms. Resize handle elastic removed — snaps directly. Context menu appears at 0ms. Chrome transitions become opacity fades at 80ms.

Rules

  • The cursor is the animation. Any element the user is directly manipulating must follow the cursor at exactly 0ms with no easing. This is a categorical rule with no exceptions — any latency or easing on cursor-tracked elements makes the tool feel broken.
  • Springs are earned. Only elements at the boundary of direct manipulation — panels extending the workspace, handles releasing tension — use spring physics. General UI chrome uses CSS transitions.
  • Never animate the canvas during user input. Zoom transitions, fit-to-screen, and frame-highlight animations must only play when the user is not actively dragging, resizing, or typing.
  • Always implement prefers-reduced-motion. Panel springs snap to position, canvas transitions are instant, prototype preview enters at 0ms. The tool must remain fully functional — and for power users, the instant behavior may actually be preferable in normal operation too.

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("figma")
Set up MCP →