figma-implement-motion

Translates Figma motion and animations into production-ready application code. Use when implementing animation/motion from a Figma design — user mentions "implement this motion", "add animation from Figma", "animate this component", provides a Figma URL whose node is animated, or when `get_design_co

By figma · 739 installs

npx skills add figma/mcp-server-guide --skill figma-implement-motion

Source repository · Upstream listing

Implement Motion Overview This skill guides translation of Figma animations and transitions into runnable code (motion.dev, CSS keyframes, or framework specific libraries). Figma exposes motion through two tools: get motion context — authoritative motion tool. Returns the complete animated node inventory, precomputed code snippets (CSS @keyframes + motion.dev), fallback keyframe bindings when snippets are unavailable, and recursive timeline coordination hints ( timelineCohorts ). Source of truth for animation data and which node IDs animate. get design context — the design's structure : layout, sizing, assets, styling, Code Connect hints, screenshot context, and sometimes motion placement markers on animated elements ( data node id , and on split nodes data motion keys / data motion wrapper for / data motion transform template ). It may render an animated node as a plain element ( div , p , span , etc.) or a motion element ( motion.div ); it does not inline the animation values. The two are linked by node id, and that's the whole workflow. get motion context tells you which nodes animate and gives the keyframe values, easing, timing, and snippets. get design context tells you what those nodes look like and where they sit. For every node in get motion context.nodes , find the matching data node id in design context and merge the motion into that structure — adding or wrapping a motion.{tag} when the structural element is plain. When design context has reused a Figma component, the motion node may also include fallbackNodeId ; use it only as a fallback after trying the exact nodeId . Skill Boundaries Use this skill when the deliverable is motion code in the user's repository. If the user asks to create/edit animations inside Figma itself, switch to [figma use](../figma use/SKILL.md) and follow that skill instead. This skill currently covers animations as emitted by get motion context (snippets plus fallback keyframe tracks, including preset authored motion resolved into those forms). Broader interactive variant flows may still need product specific state handling in code. Prerequisites Figma MCP server connected and accessible. Node ID parsed from the Figma URL the user provides. URL format: https://figma.com/design/:fileKey/:fileName?node id=1 2 — extract fileKey (the segment after /design/ ) and nodeId (the value of the node id query parameter, e.g. 42 15 ). Target codebase. Motion output format adapts to stack (see [Framework Recommendations]( framework recommendations)). Tool Choice For motion implementation, use both tools with distinct roles: Situation Tool Why Understanding static structure, assets, styles, Code Connect, or visual layout get design context Gives the component/page code reference and asset URLs you need to place animated nodes correctly. Fetching animation data for any node get motion context Purpose built for motion and the source of truth for timing, easing, snippets, and keyframes. A node has motion markers ( data motion keys , data motion wrapper for ) Markers for split placement , get motion context for values Split markers tell you which tracks go on which element; the keyframes/easing/timing and animated node inventory come from get motion context . get motion context accepts recursive: true (capped at 500 nodes) when you need descendants' motion in one call. Required Workflow Step 1: Confirm static design context is available If get design context has already been called for this node, reuse that output. If not, call it normally now. Use it as the structure of record — hierarchy, sizing, styling, assets, Code Connect hints, screenshot context, and any motion placement markers it happens to include (Step 3). The animated node inventory and animation values come from get motion context (Step 2). Step 2: Fetch authoritative motion data Response shape (one entry per animated node): codeSnippets — pre generated CSS @keyframes and motion.dev strings. Use these directly. Do not regenerate them from fallback track data. keyframeBindings — bound keyframe tracks, including preset derived motion resolved into track data, included only as fallback data when both snippet formats are missing. motionSummary — one line per field natural language description of the animation. Present only when there's no snippet (keyframe bindings only motion codegen couldn't express as CSS/motion.dev). Build from it when present; ignore it whenever a snippet exists. fallbackNodeId — optional fallback id for matching componentized design context. If nodeId is an instance qualified id such as I4005:6111;30:8005 , D2R may render the reusable component body with the backing component id instead, such as 4002:3957 . In that case, fallbackNodeId is the data node id to look for if exact nodeId lookup fails. Recursive responses also include timelineCohorts — a top level array (not per node) of nodes sharing one timeline: { rootNodeId, durationMs, loopMode: 'once' 'loop' 'boomerang', memberNodeIds[] } . For coordinated multi node motion, drive all memberNodeIds from one shared lifecycle using durationMs (÷1000 for seconds) and loopMode — don't infer timing from sibling order. Implementation details that matter for LLMs: When a snippet exists, motionSummary , timelineDurationMs , and transformOrigin may be omitted to shrink the payload — the snippet already carries duration + transform origin (motion.dev duration / style={{ transformOrigin }} , or CSS animation / transform origin ) and the cohort carries durationMs . A missing field never means "no animation." Recursive responses dedupe exact duplicate snippets. A snippet may be replaced with a comment pointing to the first node with identical motion; reuse the same component, variant, class, or constants instead of writing a second animation. The MCP server infers CSS vs motion.dev snippets from clientFrameworks ; if the response only contains one snippet format, adapt that format to the user's stack rather than assuming the other format failed. Step 3: Merge static and motion context Start from get motion context.nodes , not from visible motion. tags in the static JSX. Every returned node is animated. Match each motion node back to get design context by exact nodeId / data node id first. If and only if there is no exact match, try fallbackNodeId / data node id . Fall back to node name/type and screenshot position only after both ids fail. Exact id match wins over fallbackNodeId . fallbackNodeId points at the backing component id that D2R may emit inside a reusable component. It is shared by every instance of that component. If the exact nodeId exists in design context, apply motion there and ignore the fallback. This is critical for root instance animation: one instance can rotate or move differently from another instance of the same component, and applying that motion to the shared component body would animate all instances incorrectly. Apply each motion node to the matching design context structure, keyed by data node id . The matching data node id is the structural anchor, not always the final DOM element that receives motion. Use the snippet shape and placement markers to decide whether motion goes on that exact element, a wrapper, an inner element, or an inlined SVG path. get design context may already emit motion.{tag} with values stripped, or it may emit a plain structural element ( div , p , span , component root, etc.). If it is plain and the snippet targets the element itself, convert it to the appropriate motion.{tag} or add a motion wrapper while preserving the node's text, children, classes/styles, attributes, and data node id . Load [references/examples and anti examples.md](references/examples and anti examples.md) to see examples of this merging step. Componentized child motion usually matches by fallback. When design context extracts a Figma instance into a reusable React component, children inside that component body often have backing component ids ( 4002:3957 ) while motion context reports live instance ids ( I4005:6111;30:8005 ). In this case, use fallbackNodeId to find the component body data node id , but keep the motion scoped to the rendered instance you are implementing. If there are multiple instances and only one has different root motion, exact id matching keeps that per instance motion separate. Split nodes carry a data motion keys / data motion wrapper for marker — see Handling interleaved transforms below. Preserve display: contents wrappers — unless the group itself animates. Layout transparent group wrappers come through as contents (Tailwind contents ), usually alongside a dead absolute / inset […] (those do nothing on a contents box). For a static group, keep display: contents and let the children position against the nearest real ancestor — converting the wrapper's inset into a positioned box reparents the children to a smaller box, so they render too small / shifted inward. For an animated group (the group node itself has motion), display: contents can't carry a transform — replace it with a real positioned wrapper and apply the group motion there. Load [references/gotchas.md](references/gotchas.md) before implementing this case. get motion context is the complete animated node inventory. Some animated nodes render as plain (non motion ) elements — component instance roots (plain positioning <div ), text ( <p ), masks — that still carry a data node id . Walk every node in the motion response and apply its motion to the element with the matching data node id , wrapping or converting as needed. If an animated node has no element at all in the output (e.g. an animated mask flattened into a static mask image ), don't drop it silently — leave a // TODO: <nodeId motion unsupported comment and call it out in your summary. If a node appears in motion context but not in the static JSX, add the element needed to represent it — design context code is a reference, not a complete animation inventory. On conflict between design and motion context (timing/easing/animated values), prefer get motion context . Path level SVG motion: inline the SVG and animate the real <path . When get motion context targets a vector's path ( PATH TRIM , motion.path , stroke dasharray ) but design context renders it as an <img , inline the SVG and apply the snippet to the <path , keeping the layout wrapper. Load [references/svg and path motion.md](references/svg and path motion.md) for the full how to for this case (motion.path, pathLength="1" , wrapper+path layering, CSS path trim). Handling interleaved transforms A node with both a static base transform and animated transforms is split across nested elements so the two compose correctly instead of fighting: an id less motion.div carrying data motion wrapper for="<nodeId " (the OUTER wrapper) wraps a static transform div (e.g. rotate 45 + hypot() sizing — or the wrapper itself carries data motion transform template="<css " ) which wraps the INNER node ( data node id ). Keep the wrapper static transform div inner nesting — collapsing it breaks sizing and the base transform. Place tracks by data motion keys . The wrapper's data motion keys (transform tracks — x / y / rotate / scaleX / scaleY / skewX ) go on the OUTER wrapper; the inner element's data motion keys go on the INNER element. Re apply a data motion transform template . If the wrapper carries one, set transformTemplate={( , generated) = "<css " + generated} so the animated transform composes on top of that static layout transform. Offset the animated transform by the static base (avoid double rotation). get motion context gives the node's ab