figma-implement-motion
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseImplement 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:
- — authoritative motion tool. Returns the complete animated-node inventory, precomputed code snippets (CSS
get_motion_context+ motion.dev), fallback keyframe bindings when snippets are unavailable, and recursive timeline coordination hints (@keyframes). Source of truth for animation data and which node IDs animate.timelineCohorts - — the design's structure: layout, sizing, assets, styling, Code Connect hints, screenshot context, and sometimes motion placement markers on animated elements (
get_design_context, and on split nodesdata-node-id/data-motion-keys/data-motion-wrapper-for). It may render an animated node as a plain element (data-motion-transform-template,div,p, etc.) or a motion element (span); it does not inline the animation values.motion.div
The two are linked by node id, and that's the whole workflow. tells you which nodes animate and gives the keyframe values, easing, timing, and snippets. tells you what those nodes look like and where they sit. For every node in , find the matching in design context and merge the motion into that structure — adding or wrapping a when the structural element is plain. When design context has reused a Figma component, the motion node may also include ; use it only as a fallback after trying the exact .
get_motion_contextget_design_contextget_motion_context.nodesdata-node-idmotion.{tag}fallbackNodeIdnodeId本技能指导将Figma动画与过渡效果转换为可运行代码(motion.dev、CSS关键帧或框架专属库)。
Figma通过两个工具暴露动效信息:
- —— 权威动效工具。返回完整的动画节点清单、预计算的代码片段(CSS
get_motion_context+ motion.dev)、代码片段不可用时的备选关键帧绑定,以及递归时间线协调提示(@keyframes)。是动画数据及哪些节点ID包含动画的唯一可信来源。timelineCohorts - —— 设计的结构信息:布局、尺寸、资源、样式、Code Connect提示、截图上下文,有时还包含动画元素上的动效放置标记(
get_design_context,拆分节点上的data-node-id/data-motion-keys/data-motion-wrapper-for)。它可能将动画节点渲染为普通元素(data-motion-transform-template、div、p等)或动效元素(span);但不会内联动画值。motion.div
两者通过节点ID关联,这就是完整工作流。告知哪些节点包含动画,并提供关键帧值、缓动效果、时长和代码片段。告知这些节点的外观和位置。对于中的每个节点,在设计上下文中找到匹配的,并将动效合并到该结构中——如果结构元素是普通元素,则添加或包装为。当设计上下文复用了Figma组件时,动效节点可能还包含;仅在尝试精确失败后才使用它作为备选。
get_motion_contextget_design_contextget_motion_context.nodesdata-node-idmotion.{tag}fallbackNodeIdnodeIdSkill 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 and follow that skill instead.
- This skill currently covers animations as emitted by (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.
get_motion_context
- 当交付物为用户仓库中的动效代码时,使用本技能。
- 如果用户要求在Figma内部创建/编辑动画,请切换至figma-use并遵循该技能的指引。
- 本技能目前涵盖输出的动画(代码片段加备选关键帧轨道,包括已解析为这些形式的预设动效)。更广泛的交互式变体流程可能仍需要代码中添加产品特定的状态处理。
get_motion_context
Prerequisites
前置条件
- Figma MCP server connected and accessible.
- Node ID parsed from the Figma URL the user provides. URL format: — extract
https://figma.com/design/:fileKey/:fileName?node-id=1-2(the segment afterfileKey) and/design/(the value of thenodeIdquery parameter, e.g.node-id).42-15 - Target codebase. Motion output format adapts to stack (see Framework Recommendations).
- Figma MCP服务器已连接且可访问。
- 已从用户提供的Figma URL中解析出节点ID。URL格式:—— 提取
https://figma.com/design/:fileKey/:fileName?node-id=1-2(fileKey后的片段)和/design/(nodeId查询参数的值,例如node-id)。42-15 - 目标代码库。动效输出格式会适配技术栈(参见框架推荐)。
Tool Choice
工具选择
For motion implementation, use both tools with distinct roles:
| Situation | Tool | Why |
|---|---|---|
| Understanding static structure, assets, styles, Code Connect, or visual layout | | Gives the component/page code reference and asset URLs you need to place animated nodes correctly. |
| Fetching animation data for any node | | Purpose-built for motion and the source of truth for timing, easing, snippets, and keyframes. |
A node has motion markers ( | Markers for split placement, | Split markers tell you which tracks go on which element; the keyframes/easing/timing and animated-node inventory come from |
get_motion_contextrecursive: true对于动效实现,需结合使用两个工具,各自承担不同角色:
| 场景 | 工具 | 原因 |
|---|---|---|
| 了解静态结构、资源、样式、Code Connect或视觉布局 | | 提供组件/页面代码参考和资源URL,确保动画节点放置正确。 |
| 获取任意节点的动画数据 | | 专为动效设计,是时长、缓动效果、代码片段和关键帧的可信来源。 |
节点包含动效标记( | 标记用于拆分放置, | 拆分标记告知哪些轨道应用于哪个元素;关键帧/缓动/时长及动画节点清单来自 |
当需要一次性获取后代节点的动效时,可接受(上限为500个节点)。
get_motion_contextrecursive: trueRequired Workflow
必选工作流
Step 1: Confirm static design context is available
步骤1:确认静态设计上下文可用
get_design_context(fileKey=":fileKey", nodeId="<node-id>")If has already been called for this node, reuse that output. If not, call it normally now.
get_design_contextUse 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 (Step 2).
get_motion_contextget_design_context(fileKey=":fileKey", nodeId="<node-id>")如果已针对该节点调用过,则复用其输出。否则,立即正常调用。
get_design_context将其作为记录结构——层级、尺寸、样式、资源、Code Connect提示、截图上下文,以及可能包含的任何动效放置标记(步骤3)。动画节点清单和动画值来自(步骤2)。
get_motion_contextStep 2: Fetch authoritative motion data
步骤2:获取权威动效数据
get_motion_context(fileKey=":fileKey", nodeId="<node-id>", recursive=true)Response shape (one entry per animated node):
- — pre-generated CSS
codeSnippetsand motion.dev strings. Use these directly. Do not regenerate them from fallback track data.@keyframes - — bound keyframe tracks, including preset-derived motion resolved into track data, included only as fallback data when both snippet formats are missing.
keyframeBindings - — 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.
motionSummary - — optional fallback id for matching componentized design context. If
fallbackNodeIdis an instance-qualified id such asnodeId, D2R may render the reusable component body with the backing component id instead, such asI4005:6111;30:8005. In that case,4002:3957is thefallbackNodeIdto look for if exactdata-node-idlookup fails.nodeId
Recursive responses also include — a top-level array (not per-node) of nodes sharing one timeline: . For coordinated multi-node motion, drive all from one shared lifecycle using (÷1000 for seconds) and — don't infer timing from sibling order.
timelineCohorts{ rootNodeId, durationMs, loopMode: 'once' | 'loop' | 'boomerang', memberNodeIds[] }memberNodeIdsdurationMsloopModeImplementation details that matter for LLMs:
- When a snippet exists, ,
motionSummary, andtimelineDurationMsmay be omitted to shrink the payload — the snippet already carries duration + transform-origin (motion.devtransformOrigin/duration, or CSSstyle={{ transformOrigin }}/animation) and the cohort carriestransform-origin. A missing field never means "no animation."durationMs - 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 ; if the response only contains one snippet format, adapt that format to the user's stack rather than assuming the other format failed.
clientFrameworks
get_motion_context(fileKey=":fileKey", nodeId="<node-id>", recursive=true)响应结构(每个动画节点对应一个条目):
- —— 预生成的CSS
codeSnippets和motion.dev字符串。直接使用这些内容,不要从备选轨道数据重新生成。@keyframes - —— 绑定的关键帧轨道,包括已解析为轨道数据的预设动效,仅在两种代码片段格式均缺失时作为备选数据提供。
keyframeBindings - —— 每个字段的单行自然语言动画描述。仅在无代码片段时呈现(仅含关键帧绑定的动效代码生成无法转换为CSS/motion.dev格式)。当存在时基于此构建;只要有代码片段存在,就忽略它。
motionSummary - —— 可选的备选ID,用于匹配组件化设计上下文。如果
fallbackNodeId是实例限定ID(例如nodeId),D2R可能使用基础组件ID(例如I4005:6111;30:8005)渲染可复用组件主体。这种情况下,如果精确4002:3957查找失败,nodeId就是要查找的fallbackNodeId。data-node-id
递归响应还包含 —— 一个顶层数组(非每个节点对应),包含共享同一时间线的节点:。对于协同多节点动效,使用(除以1000转换为秒)和驱动所有的共享生命周期——不要从兄弟节点顺序推断时长。
timelineCohorts{ rootNodeId, durationMs, loopMode: 'once' | 'loop' | 'boomerang', memberNodeIds[] }durationMsloopModememberNodeIds对LLM重要的实现细节:
- 当存在代码片段时,、
motionSummary和timelineDurationMs可能会被省略以缩小负载——代码片段已包含时长+变换原点(motion.dev的transformOrigin/duration,或CSS的style={{ transformOrigin }}/animation),而时间线组包含transform-origin。缺失字段绝不意味着“无动画”。durationMs - 递归响应会去重完全重复的代码片段。代码片段可能被替换为指向第一个具有相同动效节点的注释;复用相同的组件、变体、类或常量,而非编写第二个动画。
- MCP服务器从推断CSS vs motion.dev代码片段;如果响应仅包含一种代码片段格式,请适配该格式到用户的技术栈,而非假设另一种格式生成失败。
clientFrameworks
Step 3: Merge static and motion context
步骤3:合并静态与动效上下文
- Start from , not from visible
get_motion_context.nodestags in the static JSX. Every returned node is animated. Match each motion node back tomotion.*by exactget_design_context/nodeIdfirst. If and only if there is no exact match, trydata-node-id/fallbackNodeId. Fall back to node name/type and screenshot position only after both ids fail.data-node-id - Exact id match wins over .
fallbackNodeIdpoints at the backing component id that D2R may emit inside a reusable component. It is shared by every instance of that component. If the exactfallbackNodeIdexists 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.nodeId - Apply each motion node to the matching design-context structure, keyed by . The matching
data-node-idis 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.data-node-idmay already emitget_design_contextwith values stripped, or it may emit a plain structural element (motion.{tag},div,p, component root, etc.). If it is plain and the snippet targets the element itself, convert it to the appropriatespanor add a motion wrapper while preserving the node's text, children, classes/styles, attributes, andmotion.{tag}. Load references/examples-and-anti-examples.md to see examples of this merging step.data-node-id - 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 () while motion context reports live instance ids (
4002:3957). In this case, useI4005:6111;30:8005to find the component-bodyfallbackNodeId, 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.data-node-id - Split nodes carry a /
data-motion-keysmarker — see Handling interleaved transforms below.data-motion-wrapper-for - Preserve wrappers — unless the group itself animates. Layout-transparent group wrappers come through as
display: contents(Tailwindcontents), usually alongside a deadcontents/absolute(those do nothing on ainset-[…]box). For a static group, keepcontentsand let the children position against the nearest real ancestor — converting the wrapper'sdisplay: contentsinto 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),insetcan't carry a transform — replace it with a real positioned wrapper and apply the group motion there. Load references/gotchas.md before implementing this case.display: contents - is the complete animated-node inventory. Some animated nodes render as plain (non-
get_motion_context) elements — component instance roots (plain positioningmotion), text (<div>), masks — that still carry a<p>. Walk every node in the motion response and apply its motion to the element with the matchingdata-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 staticdata-node-id), don't drop it silently — leave amask-imagecomment and call it out in your summary.// TODO: <nodeId> motion unsupported - 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 . When
<path>targets a vector's path (get_motion_context,PATH_TRIM,motion.path) but design context renders it as anstroke-dasharray, inline the SVG and apply the snippet to the<img>, keeping the layout wrapper. Load references/svg-and-path-motion.md for the full how-to for this case (motion.path,<path>, wrapper+path layering, CSS path-trim).pathLength="1"
- 从开始,而非静态JSX中可见的
get_motion_context.nodes标签。返回的每个节点都是动画节点。首先通过精确motion.*/nodeId将每个动效节点与data-node-id匹配。仅当没有精确匹配时,才尝试get_design_context/fallbackNodeId。仅当两个ID都匹配失败时,才退回到节点名称/类型和截图位置。data-node-id - 精确ID匹配优先于。
fallbackNodeId指向D2R可能在可复用组件内部输出的基础组件ID。它由该组件的每个实例共享。如果设计上下文中存在精确fallbackNodeId,则在该位置应用动效并忽略备选ID。这对于根实例动画至关重要:一个实例的旋转或移动方式可能与同一组件的另一个实例不同,将动效应用于共享组件主体会导致所有实例动画错误。nodeId - 将每个动效节点应用于匹配的设计上下文结构,以为键。匹配的
data-node-id是结构锚点,并非总是接收动效的最终DOM元素。使用代码片段结构和放置标记决定动效是应用于该精确元素、包装器、内部元素还是内联SVG路径。data-node-id可能已输出剥离值的get_design_context,或输出普通结构元素(motion.{tag}、div、p、组件根等)。如果是普通元素且代码片段目标是元素本身,则将其转换为相应的span或添加动效包装器,同时保留节点的文本、子元素、类/样式、属性和motion.{tag}。加载references/examples-and-anti-examples.md查看此合并步骤的示例。data-node-id - 组件化子节点动效通常通过备选ID匹配。当设计上下文将Figma实例提取为可复用React组件时,该组件主体内的子节点通常具有基础组件ID(),而动效上下文报告的是实时实例ID(
4002:3957)。这种情况下,使用I4005:6111;30:8005查找组件主体的fallbackNodeId,但保持动效作用于正在实现的渲染实例。如果存在多个实例且只有一个实例具有不同的根动效,精确ID匹配会将每个实例的动效分开。data-node-id - 拆分节点带有/
data-motion-keys标记——参见下方的处理交错变换。data-motion-wrapper-for - 保留包装器——除非组本身包含动画。布局透明组包装器会以
display: contents(Tailwind的contents)形式输出,通常伴随无效的contents/absolute(这些在inset-[…]盒子上不起作用)。对于静态组,保留contents,让子元素相对于最近的真实祖先定位——将包装器的display: contents转换为定位盒子会将子元素重新父级到更小的盒子,导致它们渲染过小/向内偏移。对于动画组(组节点本身包含动效),inset无法承载变换——将其替换为真实的定位包装器并在此处应用组动效。实现此案例前,请加载references/gotchas.md。display: contents - 是完整的动画节点清单。某些动画节点渲染为普通(非
get_motion_context)元素——组件实例根(普通定位motion)、文本(<div>)、遮罩——但仍带有<p>。遍历动效响应中的每个节点,将其动效应用于具有匹配data-node-id的元素,按需包装或转换。如果动画节点在输出中完全没有对应元素(例如,动画遮罩被扁平化为静态data-node-id),不要静默丢弃——留下mask-image注释并在总结中指出。// TODO: <nodeId> motion unsupported - 如果动效上下文中存在某个节点但静态JSX中没有,则添加表示该节点所需的元素——设计上下文代码是参考,而非完整的动画清单。
- 当设计上下文与动效上下文存在冲突(时长/缓动/动画值)时,优先使用。
get_motion_context - 路径级SVG动效:内联SVG并动画真实。当
<path>针对矢量路径(get_motion_context、PATH_TRIM、motion.path)但设计上下文将其渲染为stroke-dasharray时,内联SVG并将代码片段应用于<img>,同时保留布局包装器。加载references/svg-and-path-motion.md查看此案例的完整操作指南(motion.path、<path>、包装器+路径分层、CSS路径修剪)。pathLength="1"
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 carrying (the OUTER wrapper) wraps a static-transform div (e.g. + sizing — or the wrapper itself carries ) which wraps the INNER node (). Keep the nesting — collapsing it breaks sizing and the base transform.
motion.divdata-motion-wrapper-for="<nodeId>"rotate-45hypot()data-motion-transform-template="<css>"data-node-idwrapper > static-transform div > inner- Place tracks by . The wrapper's
data-motion-keys(transform tracks —data-motion-keys/x/y/rotate/scaleX/scaleY) go on the OUTER wrapper; the inner element'sskewXgo on the INNER element.data-motion-keys - Re-apply a . If the wrapper carries one, set
data-motion-transform-templateso the animated transform composes on top of that static layout transform.transformTemplate={(_, generated) => "<css> " + generated} - Offset the animated transform by the static base (avoid double rotation). gives the node's absolute transform, which already includes whatever static base those divs apply. A
get_motion_contextsnippet ofrotateover a[45, 125, 125]base means the wrapper animates the offsetrotate-45(= absolute − 45), not the absolute — else the 45° applies twice and the element sits at 90° at rest. Tracks with no static base (e.g.[0, 80, 80]/xstarting at 0) pass through unchanged. See the interleaved-transform example.y - Keep layout transforms separate from Motion transforms. For every element that animates
motion.*,rotate, orscale, verify it does not also rely on Tailwind layout transforms such asskewor-translate-x-1/2for centering/positioning. Those utilities share the CSS-translate-y-1/2property that Motion.dev writes inline, so Motion's transform can erase the layout translate. If both are needed, split the element into a static layout wrapper carrying the centering/positioning transform and an innertransformelement carrying animated rotate/scale/opacity, or encode the layout offset in Motion itself (motion.*) and keep it present for every keyframe.x: "-50%"
同时包含静态基础变换和动画变换的节点会拆分为嵌套元素,以便两者正确组合而非冲突:一个无ID的带有(外部包装器),包裹带有静态变换的div(例如 + 尺寸——或包装器本身带有),后者再包裹内部节点()。保持的嵌套结构——折叠会破坏尺寸和基础变换。
motion.divdata-motion-wrapper-for="<nodeId>"rotate-45hypot()data-motion-transform-template="<css>"data-node-idwrapper > static-transform div > inner- 按放置轨道。包装器的
data-motion-keys(变换轨道——data-motion-keys/x/y/rotate/scaleX/scaleY)应用于外部包装器;内部元素的skewX应用于内部元素。data-motion-keys - 重新应用。如果包装器带有该标记,设置
data-motion-transform-template,使动画变换组合在静态布局变换之上。transformTemplate={(_, generated) => "<css> " + generated} - 通过静态基础偏移动画变换(避免双重旋转)。提供节点的绝对变换,已包含这些div应用的任何静态基础。如果
get_motion_context代码片段为rotate且基础为[45, 125, 125],则包装器应动画偏移值rotate-45(=绝对−45),而非绝对值——否则45°会应用两次,元素静止时会处于90°。无静态基础的轨道(例如[0, 80, 80]/x从0开始)保持不变。参见交错变换示例。y - 将布局变换与Motion变换分开。对于每个动画、
rotate或scale的skew元素,验证它是否还依赖Tailwind布局变换(例如motion.*或-translate-x-1/2)进行居中/定位。这些工具与Motion.dev内联写入的CSS-translate-y-1/2属性共享,因此Motion的变换会覆盖布局平移。如果两者都需要,将元素拆分为带有居中/定位变换的静态布局包装器,以及带有动画旋转/缩放/透明度的内部transform元素,或者在Motion中编码布局偏移(motion.*)并确保每个关键帧都存在该偏移。x: "-50%"
Step 4: Apply the motion in code
步骤4:在代码中应用动效
- motion.dev present in snippets? Use the motion.dev code verbatim for React targets. Import from — unless the codebase already uses another motion library (Framer Motion, React Spring, GSAP), in which case adapt the snippet to it. Load references/framework-recommendations.md when adapting to another stack or choosing a library.
motion/react - CSS keyframes present? Use for vanilla/non-React targets, or when the codebase has no React motion library.
- No snippets (keyframe-bindings-only)? Build equivalent motion.dev/CSS from +
keyframeBindings, taking loop timing from the cohort'smotionSummary/durationMsand readingloopMode/ duration from the structured fields. Rare — snippets are normally present, including for SwiftUI/iOS (which get the CSS format).transformOrigin
- 代码片段中包含motion.dev? 对于React目标,直接使用motion.dev代码。从导入——除非代码库已使用其他动效库(Framer Motion、React Spring、GSAP),此时需将代码片段适配为该库的格式。适配到其他技术栈或选择库时,请加载references/framework-recommendations.md。
motion/react - 包含CSS关键帧? 用于原生/非React目标,或代码库无React动效库时。
- 无代码片段(仅含关键帧绑定)? 从+
keyframeBindings构建等效的motion.dev/CSS代码,从时间线组的motionSummary/durationMs获取循环时长,并从结构化字段读取loopMode/ 时长。这种情况很少见——通常会有代码片段,包括SwiftUI/iOS(获取CSS格式)。transformOrigin
Step 5: Validate
步骤5:验证
- Read the component's existing motion imports/conventions before adding new ones. If the user already uses Framer Motion / React Spring / anime.js, adapt rather than forcing motion.dev.
- Spot-check one animation runs end-to-end (reload, observe, iterate) before batching changes across many nodes.
- Load references/gotchas.md, which covers specific bugs and edge cases seen in Figma motion output, and correct any such cases in the generated code.
- 添加新导入前,先阅读组件现有的动效导入/约定。如果用户已使用Framer Motion / React Spring / anime.js,请适配而非强制使用motion.dev。
- 在批量修改多个节点前,抽查一个动画是否能完整运行(重新加载、观察、迭代)。
- 加载references/gotchas.md,其中涵盖Figma动效输出中出现的特定bug和边缘情况,并在生成代码中修正此类问题。
Critical Rules
关键规则
These are the general principles. Specific gotchas (rotation pivots, HOLD semantics, color interpolation, etc.) live in the categorized references. When a linked reference is mentioned in this skill text and the situation applies, load that file before continuing.
- Respect the tool's output's values, not its layout. Preserve the exact timing, easing, keyframe values, and from
transformOrigin— don't regenerate them fromcodeSnippetsor the structured fields when snippets exist (regenerating loses fidelity on custom bezier easings, spring approximations, and overshoot values).keyframeBindingsis per element: apply each scaling/rotating node's own — including nested scalers, not just the outer wrapper — or the element pivots from the default center and grows/spins from the wrong corner (see the per-element-transformOriginexample). But the snippet is one node's data, not a copy-paste template: when many nodes share it, factor it per Rule 7 instead of pasting the block N times.transformOrigin - Match the user's existing motion stack. Read the component's imports and any sibling animations before adding dependencies. If the user already has Framer Motion, React Spring, anime.js, GSAP — adapt the output to their stack rather than forcing motion.dev.
- Honor . Any motion added must soften or disable under
prefers-reduced-motion— typically skip the@media (prefers-reduced-motion: reduce)(render the initial/resting state) or cut the duration to near-zero. This is an accessibility default, not an opt-in.animate - Validate one animation end-to-end before batching. Build, reload, and watch one full timeline loop — confirm each animated node appears at the time its keyframe track says it should. "Renders without error" is not "renders correctly." Motion failures compound when you batch — a wrong easing on one node is easy to spot; the same bug across twenty nodes is hours of untangling.
- Don't fabricate motion. If a node has no motion data in the response, leave it static. Do not borrow easing/duration defaults from elsewhere in the design, and do not auto-animate "because the rest of the component is animated."
- Don't download an asset just to it.
Read/get_design_contextreturn assets as URLs (get_motion_context), often SVG. Reference the URL directly where the consumer fetches it (an/api/mcp/asset/..., CSS<img src>, an asset import), orbackground-imageone to inline its contents (e.g. inline the SVG and render viacurlon SwiftUI). The important exception is path-level SVG motion: if the motion snippet targets a path inside an SVG asset, inline the SVG and animate the real path instead of leaving it behind anNSImage(data:). Don't download an asset and feed the file to the<img>tool: SVG isn't a Read-able image format, so the read is rejected and wasted — and a file tool that doesn't detect SVG-as-image can stall the loop on it.Read - Factor out repeated motion — never copy-paste the snippet per element. Many nodes usually share the same animation differing only by a stagger delay, offset, or target value. Implement the shared motion once — a reusable animated component or a object parameterized by the values that vary — render from a mapped array (
variants), and pull repeated literals (durations, easing arrays, offsets) into named constants. The animation's values stay verbatim from the snippet (Rule 1); the code stays DRY. The same transition object pasted 15+ times (800 lines that should be 150) is a low-quality result — fidelity and maintainability are both graded.items.map(...)
这些是通用原则。特定陷阱(旋转支点、HOLD语义、颜色插值等)在分类的参考资料中。当技能文本中提及某个链接参考资料且场景适用时,请先加载该文件再继续。
- 尊重工具输出的值,而非布局。保留中精确的时长、缓动效果、关键帧值和
codeSnippets——当存在代码片段时,不要从transformOrigin或结构化字段重新生成(重新生成会丢失自定义贝塞尔缓动、弹簧近似和过冲值的保真度)。keyframeBindings是每个元素专属:应用每个缩放/旋转节点自己的变换原点——包括嵌套缩放器,而非仅外部包装器——否则元素会从默认中心旋转/缩放,导致错误的角落(参见每个元素的transformOrigin示例)。但代码片段是单个节点的数据,而非复制粘贴模板:当多个节点共享同一代码片段时,请遵循规则7提取复用,而非粘贴N次代码块。transformOrigin - 匹配用户现有的动效技术栈。添加依赖前,先阅读组件的导入和任何兄弟动画。如果用户已使用Framer Motion、React Spring、anime.js、GSAP——将输出适配到他们的技术栈,而非强制使用motion.dev。
- 尊重。添加的任何动效必须在
prefers-reduced-motion下弱化或禁用——通常跳过@media (prefers-reduced-motion: reduce)(渲染初始/静止状态)或将时长缩短至接近零。这是无障碍默认设置,而非可选功能。animate - 批量修改前先验证一个动画完整运行。构建、重新加载并观察一个完整的时间线循环——确认每个动画节点在关键帧轨道指定的时间出现。“无错误渲染”不等于“正确渲染”。批量修改时动效故障会叠加——一个节点的缓动错误容易发现;二十个节点的相同错误则需要数小时排查。
- 不要编造动效。如果响应中某个节点无动效数据,保持其静态。不要从设计的其他地方借用缓动/时长默认值,也不要“因为组件其他部分有动画”而自动添加动画。
- 不要仅为而下载资源。
Read/get_design_context以URL形式返回资源(get_motion_context),通常是SVG。直接在消费者获取资源的地方引用该URL(/api/mcp/asset/...、CSS<img src>、资源导入),或background-image一个资源以内联其内容(例如,内联SVG并在SwiftUI上通过curl渲染)。重要例外是路径级SVG动效:如果动效代码片段针对SVG资源内的路径,内联SVG并动画真实路径,而非将其留在NSImage(data:)后面。不要下载资源并将文件提供给<img>工具:SVG不是可读取的图像格式,因此读取会被拒绝且浪费时间——无法检测SVG为图像的文件工具会在此处停滞循环。Read - 提取重复动效——绝不为每个元素复制粘贴代码片段。许多节点通常共享相同的动画,仅在 stagger 延迟、偏移或目标值上有所不同。仅实现一次共享动效——一个可复用的动画组件或参数化的对象——从映射数组(
variants)渲染,并将重复的字面量(时长、缓动数组、偏移)提取为命名常量。动画的值严格保留代码片段中的内容(规则1);代码保持DRY(不重复)。将相同的过渡对象粘贴15+次(800行代码应精简为150行)是低质量结果——保真度和可维护性都会被评分。items.map(...)
Framework Recommendations
框架推荐
Rule 2 covers the general posture: prefer the user's existing stack. When none exists, defaults:
- React: motion.dev (the package). The tool returns motion.dev code directly — use it.
motion - Vanilla / non-React web: CSS with
@keyframesshorthand, returned directly by the tool.animation - SwiftUI: Native modifiers, translated from the CSS snippet (
.animation(...)emits no SwiftUI code, but SwiftUI/iOS clients still get the CSS format; fall back toget_motion_context/keyframeBindings/ cohort only when snippet-less). Use only real SwiftUI APIs — no modifier takes a Figma/CSS easing directly, so load references/framework-recommendations.md, map the easing to its SwiftUI equivalent, and verify rather than invent. This path is evolving; confirm with the user if unsure.motionSummary
For established effect classes, prefer a library over hand-rolled CSS. Effects like glass/glassmorphism, confetti, particle systems, physics-based interactions, and scroll-linked motion have battle-tested library implementations that handle cross-browser quirks, accessibility, and performance far better than generated keyframes. Load references/framework-recommendations.md for the full library-by-effect-class table. Surface these as recommendations, not mandates — the user decides.
规则2涵盖了通用原则:优先使用用户现有的技术栈。当没有现有技术栈时,默认选择:
- React:motion.dev(包)。工具直接返回motion.dev代码——直接使用。
motion - 原生 / 非React Web:CSS +
@keyframes简写,工具直接返回。animation - SwiftUI:原生修饰符,从CSS代码片段转换而来(
.animation(...)不输出SwiftUI代码,但SwiftUI/iOS客户端仍会获取CSS格式;仅当无代码片段时,才退回到get_motion_context/keyframeBindings/ 时间线组)。仅使用真实的SwiftUI API——没有修饰符直接接受Figma/CSS缓动,因此请加载references/framework-recommendations.md,将缓动映射为SwiftUI等效项并验证,而非自行发明。此路径仍在演进;如有疑问请与用户确认。motionSummary
对于已确立的效果类别,优先使用库而非手动编写CSS。诸如玻璃态/毛玻璃、彩屑、粒子系统、基于物理的交互和滚动关联动效等效果,经过实战检验的库实现能更好地处理跨浏览器兼容性、无障碍和性能,远优于生成的关键帧。加载references/framework-recommendations.md查看按效果类别划分的完整库表。将这些作为建议而非强制要求——由用户决定。
Examples
示例
Load references/examples-and-anti-examples.md when you need worked examples or failure patterns. It covers the simple merge flow, plain text elements that need added, interleaved static+animated transforms, SVG path-level motion, and anti-examples for DOM rebuilding, node-id/position drift, and missing per-element .
motion.*transformOrigin需要已完成的示例或失败模式时,请加载references/examples-and-anti-examples.md。它涵盖了简单合并流程、需要添加的普通文本元素、交错静态+动画变换、SVG路径级动效,以及DOM重建、节点ID/位置偏移和缺失每个元素的反例。
motion.*transformOriginReferences
参考资料
Six deep dives, fetched on demand. General frontend concerns (performance, units, accessibility mechanics) are handled by the critical rules above — these references focus on Figma-specific signal only. If this skill names one of these files in an inline instruction, load that file before continuing with that part of the task.
- references/examples-and-anti-examples.md — worked examples and failure patterns. Load when applying the merge workflow, handling interleaved transforms, or checking whether a generated implementation has rebuilt the DOM, swapped node positions, or dropped .
transformOrigin - references/gotchas.md — Figma-specific motion bugs and their fixes. Rotation/scale origin on nested groups, HOLD easing semantics, CUSTOM_SPRING preservation, independent axis scaling ambiguity, color interpolation. Load when troubleshooting unexpected runtime behavior. Always load references/motion-lint-rules.md alongside this file — gotcha entries reference specific lint rules that must be surfaced to the user.
- references/svg-and-path-motion.md — implementing motion that targets an SVG vector path (inline the asset, ,
motion.path, wrapper+path layering, CSS path-trim). Load when a vector's snippet targets the path, not a wrapper transform.pathLength="1" - references/framework-recommendations.md — motion.dev, CSS keyframes, SwiftUI defaults, library-by-effect-class table (glass, confetti, particles, physics, scroll-linked). Load before hand-rolling an effect.
- references/unsupported-and-fallbacks.md — Figma motion features that don't export cleanly today (text animations, path animations, masks/booleans, variants/transitions). Includes video/lottie fallback guidance. Load when the tool response seems incomplete. Always load references/motion-lint-rules.md alongside this file — unsupported entries reference specific lint rules that must be surfaced to the user.
- references/motion-lint-rules.md — Linting rules: known export limitations (errors and warnings) that must be surfaced to the user. Load when generating motion code to check whether any active limitations apply.
六个深度指南,按需获取。通用前端问题(性能、单位、无障碍机制)由上述关键规则处理——这些参考资料专注于Figma特定信号。如果本技能在 inline 指令中提及这些文件之一,请先加载该文件再继续任务的相应部分。
- references/examples-and-anti-examples.md —— 已完成示例和失败模式。应用合并工作流、处理交错变换或检查生成的实现是否重建DOM、交换节点位置或丢失时加载。
transformOrigin - references/gotchas.md —— Figma特定动效bug及其修复。嵌套组的旋转/缩放原点、HOLD缓动语义、CUSTOM_SPRING保留、独立轴缩放歧义、颜色插值。排查意外运行时行为时加载。请始终同时加载references/motion-lint-rules.md——陷阱条目引用了必须向用户展示的特定 lint 规则。
- references/svg-and-path-motion.md —— 实现针对SVG矢量路径的动效(内联资源、、
motion.path、包装器+路径分层、CSS路径修剪)。当矢量的代码片段针对路径而非包装器变换时加载。pathLength="1" - references/framework-recommendations.md —— motion.dev、CSS关键帧、SwiftUI默认值、按效果类别划分的库表(玻璃态、彩屑、粒子、物理、滚动关联)。手动编写效果前加载。
- references/unsupported-and-fallbacks.md —— 目前无法清晰导出的Figma动效功能(文本动画、路径动画、遮罩/布尔运算、变体/过渡)。包含视频/Lottie备选方案指引。当工具响应看似不完整时加载。请始终同时加载references/motion-lint-rules.md——不支持的条目引用了必须向用户展示的特定 lint 规则。
- references/motion-lint-rules.md —— Lint规则:必须向用户展示的已知导出限制(错误和警告)。生成动效代码时加载,检查是否存在任何适用的限制。