mcpField
A
animate-ui

animate-ui

github.com/imskyleen设计与 UI4.7k406 次安装1 天前更新
代码仓库

关于 animate-ui

面向 React 与 Vue 项目的动画组件技能,通过 CLI 或替换导入,将 Motion 动画应用于现有 UI 原语。它还提供动画选型、迁移、复核及交互效果复现指引。

主要功能

  1. 01为 UI 原语添加 Motion 动画
  2. 02CLI 转换 shadcn/ui 组件
  3. 03支持 React 与 Vue
  4. 04提供迁移和动画复核指南
  5. 05按交互与动效风格选方案

适用场景

  1. 01为 shadcn/ui 组件添加动画
  2. 02在 Radix UI 等原语上启用动效
  3. 03迁移 Animate UI v1 项目
  4. 04检查动画卡顿或复现参考效果

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/imskyleen/animate-ui --skill animate-ui

安装后在 Claude Code 中输入/animate-ui确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

NOTICE.md
references/
base-libraries.md
catalog.md
feel.md
icons.md
migrate.md
references/interactions/
banner.md
count-badge.md
expanding-card.md
field-error.md
filter-grid.md
form-steps.md
list.md
README.md
reorder.md
skeleton.md
status-button.md
swipe-row.md
references/recipes/
card.md
css-driven.md
disclosure.md
indicator.md
menu.md
modal.md
overlay.md
page.md
palette.md
press.md
README.md
route.md

详细介绍

Animate UI

Animate UI 为构建组件所用的基础组件添加动画。其软件包 @animate-ui/react 为每个 Radix UI、Base UI 和 React Aria 基础组件提供与对应库完全一致的 API,并内置 Motion,因此只需替换导入语句即可。在 Vue 中,@animate-ui/vue 也为 reka-ui(shadcn-vue 的基础组件)提供同样的功能,并搭配 Motion for Vue(motion-v)。

它的动效富有弹性、灵动而又协调:几乎所有元素都采用弹簧动画,每种动效角色都有一个对应数值(即令牌),而补间动画则根据元素的用途选择曲线。它借鉴了 Emil Kowalski 的实用规则(以触发位置为锚点的变换原点、进入和离开时使用 ease-out、按压反馈、用轻微模糊衔接切换;参见 NOTICE.md),但并不那么严格:弹簧可以稍微过冲,弹出元素可以从 0.7 开始,键盘交互也可以像指针交互一样播放动画。

1. 选择路径

需求涉及…操作
Vue 或 Nuxt 项目(shadcn-vue、reka-ui、.vue 文件)references/vue.md,然后按其中的路径操作
shadcn/ui 组件(项目中有 components.json)A. 运行 CLI
基于 Radix UI、Base UI 或 React Aria 手工构建的组件B. 替换导入语句
自定义标记、纯标记、其他库,或 Animate UI 未涵盖的基础组件C. 按规则添加动画
Animate UI v1 项目(包含 components/animate-ui/... 文件)references/migrate.md
已使用 Animate UI 的应用:更快、更慢、更舒缓§ 节奏
检查现有动效、排查缓慢或卡顿的动画references/review.md
修复现有动效中的一条规则(减少动效、循环动画)C,仅执行第 8 步和对应规则
需要复现的动画:链接、视频、GIF、CSS 或 JS 代码片段references/reproduce.md
用文字描述的感觉(“更灵敏”“更柔和”“有弹性”)或令牌对应的 CSSreferences/feel.md
按名称指定的交互:加载按钮、列表、滑动、横幅……C,参阅 references/interactions/
需要添加动画的图标,来源不限于某个库或 SVGreferences/icons.md

手动编写动效前,请先查看 references/catalog.md:其中已提供入场动画、滚动显现、计数器、文本效果、按钮、背景和图标,直接使用比重新实现更好。

2. 安装

路径 A、B 和 C 会导入 @animate-ui/react 和 motion(只有在路径 C 编写 Motion 代码时才需要导入;仅使用 CSS 或 Tailwind 的请求不会添加任何包);根据项目的锁文件,使用对应的包管理器添加依赖:pnpm-lock.yaml → pnpm add,yarn.lock → yarn add,bun.lock/bun.lockb → bun add,否则使用 npm install。使用相同方式运行 CLI(pnpm dlx、yarn dlx、bun x、npx;下文命令使用 pnpm)。组件已经使用的原语库(radix-ui、@base-ui/react、react-aria-components)应保留安装:@animate-ui/react 构建于这些库之上。CLI 和替换导入都不会安装任何内容。如果 @animate-ui/react 既不在 package.json 中,也不在 node_modules 中,animate 会发出警告;如果 migrate 所需的包(@base-ui/react)不在 package.json 中,migrate 会发出警告。如果无法安装,请手动将条目添加到 package.json 的 dependencies 中,并在输出中给出安装命令(其余 peer 依赖均为可选):"@animate-ui/react": "^2.0.0" 和 "motion": "^14.0.0"(@animate-ui/react 所需的版本;如果现有的 motion 条目允许使用 14,则保持原样,包括 "latest" 和 "*")。

Tailwind 的版本在两处会产生影响(与 Motion 的 transform 并列使用居中类,以及 hover: 是否仅对精细指针生效):如果 package.json 中的 tailwindcss 版本是 4,或者 CSS 中有 @import "tailwindcss",则为 v4;如果有使用 tailwindcss 3 的 tailwind.config.*,则为 v3;只有 v4 才支持的类语法(裸 data-open: 变体、h-(--x) 这类 (--var) 值)也表示 v4。如果没有明确依据,则采用两者都适用的形式:使用外边距居中(inset-0 m-auto h-fit,已实现居中的包装元素保持不变)、使用 [@media(hover:hover)_and_(pointer:fine)]:hover: 实现悬停效果,并为悬停边框指定明确的颜色(v3 中裸 border 是灰色,v4 中则使用文本颜色)。

3. 闸门:动效的幅度

闸门(§ Gate,C 部分)决定组件的动效幅度,以及何时停止:

  • 明确的请求始终照办。 如果有人要求为某个元素添加动效,就添加动效:闸门只决定采用哪种形式;对于全天候使用的组件,选择最简短的形式(例如,按 ⌘K 打开的命令面板快速淡入并轻微缩放,不让用户等待),并用一行说明原因。绝不要在收到明确请求时回答说不加动效。
  • 开放式请求(“让页面更有生气”“你觉得这个动效怎么样”)可以减少动效,也可以在动效只会增加等待时完全不加动效。对于已经带有动效的内容,“检查一下,然后修好它”属于明确请求:保留动效,并采用合适的形式。
  • 未指定级别时,先询问级别。 A、B、迁移、§ Pace 或评审后的修复无需询问:替换就是完整答案(即使请求提到了进入和退出),而评审后的修复应让元素保留合适形式的动效。请求中任何位置出现级别或感受词,都足以说明需求,即使它出现在称赞或指定交互旁边。“循环”“淡入”或“拖动以关闭”指定了效果:以该效果为主,采用微妙级别。否则,如果请求要求为某个元素添加动效,却没有指定级别、感受或参考(“anime ma liste”“给这个按钮添加动效”),则在编写任何代码前先问一个问题:想要哪个级别,微妙、更多还是表现力强(references/feel.md,§ Levels);分别用几个词说明应用到该组件上会是什么样,并将闸门建议的选项标为推荐。链接、视频或“像 Animate UI 的 sheet 一样”也算作回答。如果无人能回答(例如非交互式运行),则采用微妙级别,并在输出中说明。感受词(“vivant”“calm”“snappy”)会在微妙级别设定感受(feel.md),除非该词描述的是动效幅度(“更多动效”“弹跳感”“表现力强”)。配方和目录中的默认值均按微妙级别编写。“不错”“做得不错”“酷”单独出现时只是称赞,不代表感受;单独指定某种交互(滑动、重排、骨架屏)也没有指定级别:需要询问。
  • 混合指定多种路径的请求(例如 shadcn/ui 对话框和手写列表)应分别采用各组件自己的路径;需要询问级别时,只针对 C 组件询问。
  • 在 A 中,请求会指定组件(“全部”表示处理所有组件,--exclude 会排除部分组件);在 B 中,则只处理指定的组件。两者都不会缩短动效:基础元素自带令牌。迁移(references/migrate.md)和 § Pace 不适用此规则。

A. shadcn/ui 组件:运行 CLI

pnpm add @animate-ui/react motion
pnpm dlx @animate-ui/cli@latest animate <component> --dry-run   # preview
pnpm dlx @animate-ui/cli@latest animate <component>             # or: animate all

<component> 是 ui 文件夹中对应文件的名称(dropdown-menu,大小写不限,可带或不带 .tsx);可以连续指定多个组件,all --exclude sidebar,calendar 则会跳过指定组件。CLI 会通过 components.json 查找组件,将它们的基础组件导入(radix-ui、@radix-ui/react-*、@base-ui/react[/…]、react-aria-components、vaul、input-otp、@shadcn/react/questionnaire)替换为 @animate-ui/react,并移除 Motion 会取代的 CSS 动画类(tw-animate 工具类、Base UI 的 data-starting-style/data-ending-style 过渡、React Aria 的 data-entering/data-exiting、Base UI 的 CSS 变量高度;Radix 的 h-(--radix-accordion-content-height) 会保留,没有影响)。同一导入中 Animate UI 不会添加动画的部分(例如 Tabs 根组件、Slot)也会一起移动:@animate-ui/react 会原样重新导出它们。它适用于所有 shadcn 基础组件和样式,可重复运行,且会列出当前还不会添加动画的基础组件。不要手动编辑 CLI 处理的内容;如果输出看起来不对,请报告。

在 Tailwind 3 中,它还会通过边距重新调整模态框的位置(inset-0 m-auto h-fit),而不是使用 Motion 的变换会抵消的平移类;它会从 package.json、components.json、配置文件或文件自身的类语法中读取版本。它有意保留的内容包括:animate-none、animate-none!、origin-(--transform-origin) 等(Motion 通过内联样式添加动画,因此二者不会冲突,而原点变量是 Motion 扩展动画的起点),以及普通的 Tailwind 过渡,例如 transition-colors 或触发器上的 transition-all(它们用于颜色和悬停反馈,而非 Motion 现在负责的进入或退出动画;移动指示器会自行复制并隐藏选中状态的外观)。

B. 基于 Radix UI、Base UI 或 React Aria 构建:替换导入

添加这些包(§ Install),然后替换:

import { Dialog, Slot } from 'radix-ui'; // before
import { Dialog, Slot } from '@animate-ui/react/primitives/radix-ui'; // after (re-exports all of radix-ui)
import * as DialogPrimitive from '@radix-ui/react-dialog'; // before
import * as DialogPrimitive from '@animate-ui/react/primitives/radix-ui/dialog'; // after
import { Dialog } from '@base-ui/react/dialog'; // before
import { Dialog } from '@animate-ui/react/primitives/base-ui/dialog'; // after
import { Dialog, Field } from '@base-ui/react'; // before
import { Dialog, Field } from '@animate-ui/react/primitives/base-ui'; // after (re-exports all of Base UI)
import { Popover, Button } from 'react-aria-components'; // before
import { Popover, Button } from '@animate-ui/react/primitives/react-aria'; // after (re-exports all of React Aria)

遵循库本身的导入风格:聚合导入(radix-ui、@base-ui/react、react-aria-components)保留其导入项,只更改来源;按包或子路径导入(@radix-ui/react-*、@base-ui/react/*、vaul → radix-ui/drawer、input-otp、@shadcn/react/questionnaire → @animate-ui/react/primitives/questionnaire)则使用对应原语的路径。整个导入都会迁移,即使只要求迁移其中一部分:Animate UI 不提供动画的部分会原样重新导出。对于 Animate UI 不提供动画的 Base UI 子路径(@base-ui/react/field),则继续使用 Base UI。

然后移除组件自身的 CSS 动画类,避免它们与 Motion 动画叠加:tw-animate 工具类(animate-in、animate-out、fade-*、zoom-*、slide-in-from-*、slide-out-to-*,以及与它们搭配的 duration-*)、Base UI 的 data-starting-style:*/data-ending-style:* 过渡、React Aria 的 data-entering:*/data-exiting:*(以及 entering:/exiting:),还有高度关键帧(animate-accordion-down/up、animate-collapsible-*;Base UI 的 h-(--accordion-panel-height) 也要移除,但 Radix 的 h-(--radix-accordion-content-height) 可以保留:动画内容会将该变量设为 auto)。其他内容全部保留:各部分、属性、数据属性、forceMount/keepMounted、渲染属性、定位类,以及各项自身的高亮或聚焦背景(data-highlighted:bg-*、focus:bg-accent):滑动高亮会覆盖它们。使用 Tailwind v4 时,居中的模态框可以保留 -translate-x-1/2 -translate-y-1/2(translate 属性会与 Motion 的 transform 组合);使用 Tailwind v3,或无法判断版本时(§ Install),它们会互相冲突,因此应改用外边距居中,而不是变换:将 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 替换为 inset-0 m-auto h-fit(宽度类保留)。会进出场的动画部分(内容、遮罩、指示器)接受两个可选属性:transition 和 exitTransition(Motion Transition);只会移动的部分(开关滑块、滑块控件、进度条)接受 transition。例外是 Animate UI 自带的即插即用组件(input-otp、@shadcn/react/questionnaire):它们只接受各自包的属性,不接受上述两个属性(AnimateUIProvider 的 tempo 和问卷检查项的 checkAnimation 仍然适用)。对于 QuestionnaireItem,请移除 shadcn/ui Animated Items 示例中的 tw-animate 类(data-active:animate-in、data-active:fade-in-0、data-active:slide-in-from-bottom-2、data-active:duration-300):这些类会与步骤的入场动画叠加。

动画速度

如需同时加快或减慢所有动画,请用 AnimateUIProvider(@animate-ui/react/provider,一个客户端组件)包裹应用,并设置 tempo:(在 Next.js 根布局中,将它放在 <body> 内并包裹 {children}。)时长和延迟会乘以该值,弹簧动画会在新的时间尺度上保持其形状,包括减少动态效果时的淡入淡出。“稍微”调整或没有指定调整幅度(“快一点”“感觉迟钝”)时,调整一级:减慢设为 1.15,加快设为 0.85;“快很多”或“慢很多”时,设为 1.3 或 0.7;如果再次提出要求,就从当前值再调整一级(1.15 → 1.3);范围保持在 0.5–2 之间。不使用该 Provider 时,会应用默认值(tempo 为 1)。如果“更快”或“迟钝”指整个应用,就调整此值;如果只指某个组件,请参阅 feel.md。它会影响所有 Animate UI 原语,以及通过 useTempoTransition() 或 useMotionTokens() 读取这些令牌的代码;应用自身使用手写值的动画(Motion 属性、Tailwind duration-*、CSS 过渡)不会受其影响:请在输出中列出这些文件,并提出将它们改为使用这些令牌。

C. 其他情况:遵循规则添加动画

  1. 了解技巧。 Motion 的文档和示例(motion.dev)展示了大多数交互的构建方式;如果工具能够读取这些内容,就在那里了解相关技巧,然后使用 Animate UI 的令牌和规则来编写。将结果呈现为用户自己的动画,而不是别人的示例。
  2. 先查目录:如果 references/catalog.md 中已有相关内容(计数器、入场、滚动显现、文本效果、按钮效果),就使用它。只要 Animate UI 组件能胜任(文字轮播、标签页、复制按钮、按下状态的按钮、计数器),就使用该组件,而不是手动编写动画,并完整使用它及其动画部分:标签页列表使用 TabsHighlight,下方变化的内容使用 TabsContents,并为每个值配置一个 TabsContent,绝不要手动切换面板来代替。如果组件的布局不合适(Rotating Text 是块级元素),就调整布局或周边属性(align、inline-block 样式),不要替换它。
  3. 先过关卡(见下文),然后对组件分类,并从匹配的配方开始:覆盖层、菜单、控件、卡片、Toast、页面和页面过渡使用 references/recipes/<kind>.md(见下表);按名称提出的交互(状态按钮、可添加和移除项目的列表、拖动排序、卡片展开为视图、骨架屏、计数徽标、错误字段、滑动删除、筛选网格、表单步骤、横幅)使用 references/interactions/。
  4. 如果组件渲染了 Radix UI、Base UI 或 React Aria 覆盖层,优先组合使用 Animate UI 原语,而不是自行重新接线 presence。手写覆盖层应保持手写,除非用户提出要求:为它添加动画并不意味着要把它迁移到某个库(那会改变焦点、模态行为和关闭方式),因此应按原样为它接入 Motion,方式参见 references/base-libraries.md 中的纯标记示例,并在输出中说明原语能带来什么。如果用户要求在手工构建的抽屉上实现拖动或滑动,就算提出了迁移要求:将它迁移到 @animate-ui/react/primitives/radix-ui/drawer(import { Drawer }:使用 Drawer.Root 并配置 direction,以及 Drawer.Portal、Drawer.Overlay、Drawer.Content、Drawer.Handle、Drawer.Title、Drawer.Description),如果缺少 radix-ui,就添加它,并在输出中说明变更内容(焦点会被限制在抽屉内,按 Escape 可关闭,抽屉后方的页面会停止滚动)。
  5. Motion 只能在客户端组件中运行:在编写 Motion 代码的文件中添加 'use client',并让服务端页面继续保持服务端渲染,将动画部分移到独立的客户端组件中。挂载时进入的内容在服务端 HTML 中会从隐藏状态开始;仅对首屏以下的内容或装饰内容这样处理,不要让页面首屏的主要文本处于隐藏状态。如果用户明确要求为可能出现在首屏中的内容添加动画,就照做,并在输出中说明内容会在 hydration 完成前保持隐藏。会因减少动态效果偏好或屏幕尺寸而变化的标记,应使用在 hydration 时提供服务端取值的 Hook 读取这些状态(useShouldReduceMotion()、带服务端快照的 useSyncExternalStore);渲染时绝不要使用 Motion 的 useReducedMotion() 或 window.matchMedia,否则 React 会丢弃服务端 HTML 并重新渲染页面。
  6. 用户描述动效感受(“利落”“平静”“更有弹性”)时,选取数值前先阅读 references/feel.md。对于不使用 Motion 的 CSS(样式表、Tailwind、服务端组件),在项目目录中运行 node <skill>/scripts/css-timing.mjs <token> 输出令牌的时序值(该脚本随此技能提供),以便加载项目的 @animate-ui/react;如果没有安装该包,就将 tokens.md 中的 tween 令牌值写成 cubic-bezier。
  7. 所有数值都必须取自令牌(references/tokens.md):import { spring, transition, scale, blur, shift, ease, useTempoTransition, ReducedMotionConfig, useShouldReduceMotion } from '@animate-ui/react/utils/motion'。通过 useTempoTransition() 传递这些令牌(或从 useMotionTokens() 中读取),以便 AnimateUIProvider 的 tempo 设置生效。令牌已涵盖的用途不得手写弹簧参数(配方中注明的距离和缩放值除外);不要使用 ease-in(但已关闭对话框的 transition.dismiss 和菜单栏中下一项内容淡入时的 transition.handoffIn 除外),入场动画不要使用 ease-in-out。动画图标是例外:其数值定义在图标文件中(references/icons.md)。
  8. 检查 references/rules.md 中的每条规则(质量规则不可妥协;风格规则是默认要求),然后验证(§ Verify)。
关卡:是否应该添加动效?
使用频率决策
持续使用(按下、工具提示、菜单、标签页、切换开关、悬停高亮)添加快速动效:spring.press、transition.menu、spring.pop、spring.selection、spring.highlight;悬停时不缩放
偶尔使用(弹出层、选择器、对话框、底部弹层、通知、展开项)使用对应角色的令牌:界面可适当放慢(进入时使用 spring.dialog、transition.drawer 和 transition.sheet,退出时使用 transition.dismiss 和 transition.drawerOut,边缘通知使用 spring.toast)
很少使用(效果、文本、数字、图标、新手引导、成功状态)使用专属动效:自定义弹簧、更多弹跳、更长节奏(图标:icons.md)
全天通过键盘切换(命令面板、全局快捷键)使用最简形式:在 transition.menu 上使用 scale.menu,快速淡入并轻微缩放;关闭时不能更慢,也不使用交错动画;响应打开请求时可以保持即时显示

键盘导航的动效应与指针操作一致。用一个词说明目的:反馈(响应某个操作)、空间(说明元素从何处来或去往何处)、状态(说明发生了什么变化)、连续性(同一元素发生了移动)、愉悦(值得精心呈现的罕见时刻:页面进入、操作成功)。响应打开请求时,如果不符合其中任何一项,就不要添加动效。

组件类型

Recipe 和门控表适用于 C;在 B 上,原语自带动效。

类型示例Recipe
锚定浮层弹出框、工具提示、悬停卡片、预览卡片recipes/overlay.md
锚定列表下拉菜单、上下文菜单、菜单栏、选择器、组合框、导航菜单recipes/menu.md
居中浮层对话框、警告对话框recipes/modal.md
边缘浮层侧边面板、抽屉recipes/sheet.md
披露手风琴、可折叠面板recipes/disclosure.md
移动指示器标签页、切换组、分页、导航菜单指示器recipes/indicator.md
流程步骤问卷recipes/steps.md
可按压控件按钮、切换按钮、复选框、开关、单选按钮recipes/press.md
可按压表面可点击卡片、平铺项、列表行recipes/card.md
通过快捷键打开的居中列表命令面板、Spotlight 搜索recipes/palette.md
在边缘堆叠的消息,由手动构建Toast、Snackbar、固定在屏幕边缘的通知recipes/toast.md
页面内容主视觉区、各分区、功能网格、统计数据recipes/page.md
页面或路由切换页面过渡(Next.js、React Router、TanStack Router)recipes/route.md
两种状态原地切换复制 → 已复制、太阳 → 月亮、变化的标签(规则 B9)recipes/swap.md
库通过 CSS 变量驱动的手势或堆叠(Base UI Drawer、Toast)recipes/css-driven.md
简明规则
  • 动画自带减少动态效果支持:在动画树外包一层 ReducedMotionConfig,使用 useShouldReduceMotion() 将 transform 字符串置为无效果;对于挂载时播放的动画,使用 <ReducedMotionConfig remount>。淡入淡出和模糊效果保留,移动效果移除。
  • 使用 transform、opacity 和 filter(只有当内容必须推动下方内容时才使用 height)。锚定弹出层从触发器处展开(使用库提供的原点变量),根据类型从 0.7 / 0.8 / 0.9 开始;模态框保持居中。
  • 按下时使用弹簧效果缩放至 0.97(整张卡片或整行缩放至 0.985);被按下的元素绝不在悬停时缩放。悬停时改变颜色、背景、边框或阴影;在带裁剪的卡片中,图像可以放大,卡片也可以上移几个像素。
  • 缓动:进入和离开使用 ease-out;眼睛关注的变化(展开的折叠内容、覆盖层、打开的菜单、滑入的面板)使用更柔和的 ease-out;屏幕上的移动使用 ease-in-out;颜色使用 ease。绝不使用 ease-in,但关闭的抽屉会在较短的 transition.drawerOut 中加速离开边缘;关闭的对话框或面板会在稳定的推力下加速离开(transition.dismiss);菜单栏的后续内容则以缓慢起步的方式淡入,出现在离开的内容后方(transition.handoffIn)。轻微模糊可衔接内容切换。
  • 不要拉伸任何内容:使用 layout 调整尺寸的盒子,其子元素也要使用 layout(或改为动画调整其高度),以保持文本、图标和图像的比例。文本变化时应淡入淡出、模糊或滑动,绝不缩放;需要更具表现力的效果时,使用 Animate UI 的文本组件(Rotating、Morphing、Rolling Text、Sliding Number)。
  • 离场时重放入场动画(关闭的对话框、抽屉或面板会立即开始离场,并加快速度);将过渡写在 exit 目标中(离场元素会保留其最后的属性)。如果从一开始就处于打开状态,仍然要播放动画。
  • 保留库提供的元素作为动画元素(使用 Radix asChild、Base UI render、React Aria render/motion.create),这样 data-* 属性和样式才能继续生效。
  • 温热状态的工具提示立即显示(它替换的工具提示也立即消失)。
  • 菜单栏的弹出层会移动到下一个菜单并调整大小(spring.morph:以柔和的方式启动并逐渐加速,不会越过目标,同时在菜单之间保持速度),而不是先关闭再重新打开;其内容在原处变化:内容在屏幕上的位置保持不变,弹出层从其上方滑过;旧项目沿离开方向偏移 shift.handoff(6px)并淡出(transition.handoffOut),新项目则从该处沿 spring.handoff 进入,并在旧项目后方淡入(transition.handoffIn):从 File 切换到 Edit 时,File 向左离开,Edit 从右侧进入。每一帧屏幕上都只有一个弹出层,每个菜单的内容只出现一次(菜单重新打开时,离场项目会从原处返回);所有触发器共享一个高亮,并在触发器之间滑动(internal/menu-handoff.ts、internal/item-highlight.tsx)。
  • 键盘操作的动画与指针操作一致;仅在支持悬停的精确指针设备上启用悬停效果;再次触发时重新定位目标,而非重启动画;循环动画在移出屏幕时暂停。

验证

使用浏览器查看每个动画中间帧,而不只是静止状态:没有东西会从不该经过的标签上方掠过,变化前后的文字不会移动,文本始终保持基线对齐,并且每个状态发生变化的部分都会动起来(立即翻转的箭头是个问题)。从一层交接到另一层的元素(共享 layoutId,从 intro 滑入页眉)应始终位于仍在淡出的元素上方:将它的新图层置于淡出图层上方,否则它会透过遮罩显现(白色徽标在滑动过程中变灰)。

  • 在改动任何内容之前先运行类型检查,即使只替换一行也一样:其输出就是修改前的基准。
  • references/rules.md 中的检查清单里的每项质量要求均通过;注明样式偏差。
  • 减少动态效果(操作系统设置或 Playwright emulateMedia({ reducedMotion: 'reduce' })):元素仍会出现和消失,但不会移动。
  • 快速触发两次:动画会重新定位目标。键盘操作路径的动画效果与指针操作路径一致。定位和 className 仍然有效。
  • 放慢动画(DevTools Animations 设为 10%),检查起点、过冲和曲线。

如果无法使用浏览器(Agent 的 shell、CI),检查可以读取的内容。在 A 和 B 上:类型检查通过,且没有新增错误(比较修改前后的输出);差异中仅替换了导入、移除了动画类、按 § B 的要求替换了模态框的平移居中方式,并添加了包条目。基础组件自带进入和退出动画,覆盖层也包括在内。在 C 上:类型检查通过,且没有新增错误;每个值都来自令牌或配方;每个会移动的元素都有减少动态效果的处理路径(用 ReducedMotionConfig 包裹,抵消变换字符串);每个 exit 的内部都有 transition,且其上方有 AnimatePresence;没有任何动画会作用于 width、height(披露内容除外)、top、left 或 margin;并在输出中说明未在浏览器中运行。对于不使用 Motion 的 CSS,按照 feel.md 末尾的检查项操作。

输出

先给出改动(CLI 运行、导入替换或代码),然后用几行说明:

  • A, B:CLI 或替换操作改了什么、需要添加哪些包、有意保留了什么,以及类型检查的修改前后结果。没有需要移除的内容也是完整答案。
  • C:级别(按要求指定,或默认采用轻微效果)、门控结果(若缩短或跳过动画,说明原因)、使用的目录项或配方、令牌,以及更改了哪些样式默认值及原因。
  • Feel:使用的词、采取的步骤或行、写入的令牌或弹簧配置,以及保留的作用。
  • Migrate:完成和未完成的手动步骤,以及删除或保留的文件。
  • Reproduce:读取或测量了什么、采用的令牌或保留的值,以及参考资料未显示的内容。
  • Icon:文件及其放置位置、图标的作用和含义、触发方式及原因、级别、替换的用法、保留的原始文件,以及调整或搁置了 icons.md 中的哪些技术或规则。
  • Review:发现的问题(文件:行号、原因、修复方式)、排除的事实及原因,以及修复时审计的前后结果。
  • Pace:设置的节奏及其所在位置。

评论

支持 Markdown