乐于分享
好东西不私藏

给 AI 编程助手装上审美品味

给 AI 编程助手装上审美品味

Linear 的设计工程师 Emil Kowalski 最近开源了一套 AI Skills,专门解决一个让前端开发者头疼的问题:AI 写出来的 UI 代码功能没问题,但就是「差点意思」。

这条推文在 24 小时内拿到 1000+ 赞和 1000+ 收藏,说明踩这个坑的人不在少数。

问题出在哪

用 AI 写前端代码,最常见的体验是:它能跑,但看着别扭。

下拉菜单用了 ease-in 缓动,打开的时候感觉拖泥带水。弹窗从 scale(0) 开始放大,像凭空蹦出来的。按钮按下去没有任何反馈,像在按一块木板。Toast 通知用 keyframes 做入场动画,快速连续触发的时候动画互相打架。

这些细节单独看都不算 bug,但叠加在一起,整个界面就透着一股「凑合」的味道。

Emil 在 README 里写得很直接:Agents don't have great taste。AI 不知道进入动画该用 ease-out 而不是 ease-in,不知道按钮按下需要 scale(0.97) 的反馈,不知道 Framer Motion 的 x/y 简写属性在主线程繁忙时会掉帧。

他的解法:把领域经验编码成 Skill

Emil 的思路很朴素:既然 AI 缺乏设计品味,那就把品味写成规则喂给它。

他开源的 emilkowalski/skills 仓库包含 7 个 Skill,覆盖了设计工程的各个环节:

  • emil-design-eng:核心 Skill,编码了动画决策框架、组件设计原则、性能规则
  • review-animations:按 10 条不可妥协的标准审查动画代码
  • improve-animations:审计整个代码库的动画,输出优先级排序的修复计划
  • find-animation-opportunities:在 UI 中找到真正需要动效的地方
  • animation-vocabulary:用精确的词汇描述你想要的动效
  • apple-design:Apple 的界面设计和流畅动效原则,翻译到 Web 端
  • pick-ui-library:按任务类型推荐经过验证的 UI 库

安装一行命令搞定:

npx skills@latest add emilkowalski/skills

装完之后,你的 AI 编程助手在处理 UI 相关任务时,会自动加载这些规则。

动画决策框架:四个问题定生死

整套 Skill 的核心是一个动画决策框架。写任何动画代码之前,按顺序回答四个问题:

第一个问题:这个元素需要动画吗?

按使用频率判断。键盘快捷键、命令面板这类每天触发 100 次以上的操作,永远不加动画。Raycast 的命令面板就没有开关动画,因为对高频操作来说,动画只会让人觉得慢。

每天触发几十次的 hover 效果,要么去掉要么大幅缩减。偶尔出现的弹窗、抽屉、Toast,用标准动画。第一次使用的引导页、庆祝动效,可以加点惊喜感。

第二个问题:动画的目的是什么?

每个动画必须能回答「为什么动」。合法的目的包括:空间一致性(Toast 从同一个方向进出)、状态指示(按钮形变表示状态切换)、反馈(按下时缩小确认用户操作)、避免突兀变化(元素突然出现会让人吓一跳)。

如果答案是「看着酷」,而且用户会频繁看到,那就别动。

第三个问题:用什么缓动曲线?

进入/退出的元素用 ease-out,感觉灵敏。在屏幕上移动/变形的元素用 ease-in-out,加速减速自然。hover 和颜色变化用 ease。匀速运动(跑马灯、进度条)用 linear

关键一点:永远别用 ease-in 做 UI 动画。它开头慢,让界面感觉迟钝。同样 300ms 的下拉菜单,ease-outease-in 感觉快得多,因为用户第一眼就看到元素在动。

Emil 推荐用自定义贝塞尔曲线,CSS 内置的曲线太弱了:

/* 强 ease-out,适合 UI 交互 */
--ease-outcubic-bezier(0.2310.321);

/* 强 ease-in-out,适合屏幕内移动 */
--ease-in-outcubic-bezier(0.7700.1751);

/* iOS 风格的抽屉曲线 */
--ease-drawercubic-bezier(0.320.7201);

第四个问题:动画持续多久?

元素类型推荐时长
按钮按压反馈100-160ms
工具提示、小弹窗125-200ms
下拉菜单、选择器150-250ms
模态框、抽屉200-500ms

核心原则:UI 动画控制在 300ms 以内。180ms 的下拉菜单比 400ms 的感觉灵敏得多。转得快一点的 loading spinner 让应用感觉加载更快,即使实际加载时间一模一样。

十条不可妥协的审查标准

review-animations 这个 Skill 定义了一套严格的动画审查标准。Emil 的态度是:默认打回,通过靠争取。

这十条标准覆盖了从合理性到性能的各个维度:

  1. 每个动画必须有理由——空间一致性、状态指示、反馈、解释、避免突兀
  2. 频率匹配——高频操作不加动画,低频操作可以加惊喜
  3. 缓动方向正确——进入/退出用 ease-outease-in 做 UI 直接打回
  4. UI 动画 300ms 以内——超了必须有理由
  5. 变换原点正确——弹窗从触发点展开,不从中心(模态框除外)
  6. 可中断——快速触发的元素用 CSS transition 或弹簧动画,不用 keyframes
  7. 只动 transform 和 opacity——动 width/height/margin 直接打回
  8. 无障碍——prefers-reduced-motion 必须处理,hover 动画必须加媒体查询
  9. 非对称时序——按下慢(用户在做决定),释放快(系统在响应)
  10. 整体协调——动效风格和组件个性匹配

性能陷阱:Framer Motion 的硬件加速坑

这套 Skill 里有一个很多人不知道的 Framer Motion 性能陷阱。

Framer Motion 的简写属性(xyscale)不走 GPU 加速,它们用 requestAnimationFrame 在主线程上跑。当浏览器同时加载内容、执行脚本、绘制页面时,这些动画会掉帧。

// ❌ 不走 GPU 加速,主线程繁忙时掉帧
<motion.div animate={{ x: 100 }} />

// ✅ 走 GPU 加速,主线程繁忙也流畅
<motion.div animate={{ transform: "translateX(100px)" }} />

Emil 提到他在 Vercel 做 dashboard 标签页动画时遇到过这个问题。用 Shared Layout Animations 做的切换在页面加载时掉帧,换成 CSS 动画(跑在合成线程上)就解决了。

CSS 动画天然跑在主线程之外。浏览器忙着加载新页面时,CSS 动画依然流畅,Framer Motion 动画会卡。预设动画用 CSS,动态可中断的动画用 JS,这是性能最优的搭配。

pick-ui-library:别让 AI 乱选库

pick-ui-library 解决的是另一个常见问题:AI 不知道用什么库,要么手写一个 Toast 组件,要么装一个没人维护的包。

这个 Skill 按任务类型给出明确推荐,全部是 Emil 自己用过的、信任的库:

任务推荐库
无样式、可访问的 UI 组件base-ui
命令菜单(⌘K)cmdk
Toast / 通知Sonner
验证码输入input-otp
通用动画(弹簧、布局、进出场)motion (Framer Motion)
数字动画(计数器、价格)NumberFlow
实时/流式图表Liveline
通用图表recharts
拖拽dnd kit
虚拟列表Virtuoso
状态管理zustand
条件 classNameclsx
Tailwind 变体样式cva
主题切换next-themes

Skill 的设计原则是:识别任务,不是识别库名。用户说「我需要一个下拉框」,这是一个 UI 原语任务,应该推荐 base-ui,即使用户提到了别的库。先查 package.json 看项目已经用了什么,如果已经在用列表里的库,继续用它。如果用的是竞品,标记推荐但别擅自换依赖。

几个容易忽略的细节

这套 Skill 里有几个细节值得单独拿出来说。

按钮必须有按压反馈。transform: scale(0.97):active 状态。这让 UI 感觉在响应用户。缩放要微妙,0.95-0.98 之间。

永远别从 scale(0) 开始动画。 现实世界中没有东西会从零突然变大。从 scale(0.95)opacity: 0 开始,感觉自然得多。

弹窗的变换原点要感知触发位置。 下拉菜单应该从触发按钮的位置展开,不是从中心。Base UI 提供了 var(--transform-origin) 变量,直接用就行。模态框是例外,它从中心展开。

Tooltip 的延迟策略。 第一个 Tooltip 有延迟(防止误触),但一旦有一个 Tooltip 打开了,后续的应该立即显示、没有动画。这让人感觉整个工具栏都更快。

用 blur 遮盖不完美的过渡。 当两个状态的交叉淡入淡出感觉不对时,加 filter: blur(2px)。模糊把两个状态融合在一起,欺骗眼睛感知为一次平滑的变换。

对国内开发者的启示

Emil 的做法给国内 AI 辅助开发提了一个醒:AI 写代码的能力在快速提升,但「品味」这件事仍然需要人类专家来定义。

国内的 AI 编程助手(如 Cursor、Copilot 在国内的使用场景)同样面临这个问题。代码能跑,但 UI 细节粗糙。解决思路是一样的:把团队的设计规范、动效标准、组件选型偏好编码成 Skill 或 System Prompt,让 AI 在生成代码时自动遵守。

具体到实操,你可以做三件事:

  1. 整理一份团队 UI 规范文档,把缓动曲线、动画时长、组件选型写成明确规则
  2. 把这些规则喂给 AI,不管是通过 Skill 文件、System Prompt 还是自定义指令
  3. 建立审查清单,像 Emil 的十条标准一样,每次 AI 生成的 UI 代码都过一遍

领域专家知识 + AI 执行能力,这个组合比单纯依赖 AI 的通用能力有效得多。Emil 自己也说了:AI 不会取代领域专业知识,它会放大你能从中获得的东西。

资源链接

  • emilkowalski/skills GitHub 仓库[1] — 完整源码和安装说明
  • animations.dev[2] — Emil 的动画课程,更深入的讲解
  • Agents with Taste[3] — Emil 关于 AI 品味的文章
  • easing.dev[4] — 贝塞尔曲线可视化工具
  • base-ui[5] — 无样式可访问 UI 组件库
  • Sonner[6] — Toast 通知库,npm 周下载量 1300 万+

引用链接

[1] emilkowalski/skills GitHub 仓库: https://github.com/emilkowalski/skills

[2] animations.dev: https://animations.dev/

[3] Agents with Taste: https://emilkowal.ski/ui/agents-with-taste

[4] easing.dev: https://easing.dev/

[5] base-ui: https://base-ui.com

[6] Sonner: https://sonner.emilkowal.ski