AI 工具 22 min read

emil-design-eng skill 實測:674 行規範,差異全在動態

emil-design-eng 是 Emil Kowalski 收在 MIT 授權 emilkowalski/skills 裡最長的一支 skill(674 行),把他對 easing、動畫時長、何時不該動畫與 UI 細節的判斷寫給 AI agent 看。我實測同一個下拉選單,點名後開啟 102ms 就接近不透明、關閉比開啟快,截圖卻幾乎一樣——而且不點名 3 次只觸發 1 次,載入一次約多 9.9k tokens。

emil-design-eng skill 實測:674 行規範,差異全在動態
本文目錄 · 9
懶人包
  • emil-design-eng 是 Emil Kowalski 在 MIT 授權的 emilkowalski/skills 裡放的主力 skill,674 行,是整個 repo 最長的一支。內容是他的設計工程判斷:該不該動、用哪條 easing、要多快、細節清單與審查格式。
  • 它的 frontmatter 只有 name 和一句 description,沒有 disable-model-invocation,也沒寫「什麼時候用」。我同一句需求不點名跑 3 次,只有 1 次自動載入。
  • 我叫 Claude Code(Sonnet)寫同一個下拉選單:點名 /emil-design-eng 的版本換成 cubic-bezier(0.23, 1, 0.32, 1),關閉 110ms 比開啟 170ms 快,減少動態時保留淡入淡出。Playwright 量到 opacity 到 0.9 只要 102ms,沒用 skill 的三份是 152–183ms。
  • 靜態截圖兩邊幾乎一樣,差異全在動態。代價是每次載入約多 9.9k input tokens,單次執行成本從 0.076 美元變 0.140 美元。
  • 跟 apple-design 的分工:apple-design 管手勢、彈簧與材質;emil-design-eng 管一般 UI 元件的時間、曲線與「不要動」的判斷。
  1. emil-design-eng 是什麼?
  2. 674 行裡有哪些章節?
  3. 最有料的四條判斷:該不該動、easing、時長、細節清單
  4. 實測:同一個下拉選單,有沒有用上它差在哪
  5. 它會自動觸發嗎?要吃多少 context?
  6. 跟 apple-design 差在哪、哪裡重疊?
  7. 怎麼安裝與點名?
  8. 什麼情況不適合用
  9. 延伸資源

🎛️ emil-design-eng 是什麼?

emil-design-eng 是一份給 AI coding agent 讀的 SKILL.md。作者 Emil Kowalski 寫過 toast 元件 sonner 和 drawer 元件 vaul,repo README 對這支的介紹是「主要講動畫,也有一些設計建議的主 skill」。

它的 frontmatter 只有兩行:

name: emil-design-eng
description: This skill encodes Emil Kowalski's philosophy on UI polish, component design, animation decisions, and the invisible details that make software feel great.

正文開頭先給 agent 一個身分:有工藝感的 design engineer,相信「大家的軟體都夠好用時,品味就是差異」。接下來 600 多行把這個品味拆成規則、數字和 code 範例。

它跟同 repo 的 animate、review-animations 這些「單一任務」skill 不同:那幾支只做一件事(從零做動畫、審查動畫),emil-design-eng 是它們共同的母體。我比對過,cubic-bezier(0.23, 1, 0.32, 1) 這條曲線在 animate、animate-expo、find-animation-opportunities 裡也都出現。

repo 的 git 紀錄顯示這支在 2026-03-16 跟 repo 一起建立,之後改過三次:03-25 調整開場訊息與格式、07-21 把 Radix UI 的範例換成 Base UI、09-15 拿掉課程連結。我讀的是 commit 2d22863 的版本。

📚 674 行裡有哪些章節?

全文 15 個 H2。我依「什麼時候會用到」分成四組:

分組章節內容
行為設定Initial Response、Core Philosophy、Review Format沒問題時只回一句固定招呼;三條信念(品味是練出來的、看不見的細節會累積、美是槓桿);審查時必須用 Before/After/Why 三欄表格
動畫決策Animation Decision Framework、Spring Animations依序回答四題:要不要動、目的是什麼、用哪條曲線、多快;何時改用 spring 與設定方式
元件與技法Component Building Principles、CSS Transform、clip-path、Gesture and Drag、Performance按鈕按壓、不要從 scale(0) 開始、popover 從觸發點展開、tooltip 延遲、@starting-style;百分比 translate、clip-path 揭露;拖曳關閉的速度門檻;只動 transform 與 opacity
品質與收尾Accessibility、Sonner Principles、Stagger、Debugging、Review Checklist減少動態、觸控裝置 hover;做 sonner 學到的六條原則;錯開進場;慢動作與逐幀檢查;11 列問題對照表
篇幅最大的是元件與技法這組,佔掉將近一半。但我覺得價值最高的是「動畫決策」那一組,因為它決定的是寫不寫、寫多少,而不是怎麼寫。

🎯 最有料的四條判斷:該不該動、easing、時長、細節清單

先問:這個東西要不要動?

skill 用「使用者一天看到幾次」來決定:

一天看到的次數例子判斷
上百次鍵盤快捷鍵、command palette 開關完全不動
幾十次hover、清單上下移動拿掉或大幅縮短
偶爾modal、drawer、toast一般動畫
很少或第一次onboarding、慶祝畫面可以加料
另一條硬規則:鍵盤觸發的動作不做動畫。它舉的例子是 Raycast 沒有開關動畫。每段動畫還要答得出目的,可接受的目的有五種:空間一致、表示狀態、解釋功能、操作回饋、避免突兀的出現與消失。答案只有「看起來很酷」而且使用者會常看到,就不做。

用哪條 easing?

決策樹很短:進場或離場用 ease-out;畫面內移動或變形用 ease-in-out;hover 和顏色變化用 ease;跑馬燈、進度條這種等速運動用 linear;拿不準就 ease-out。

它認為 CSS 內建的曲線太弱,給了三條自訂曲線:

--ease-out: cubic-bezier(0.23, 1, 0.32, 1);      /* UI 互動 */
--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);  /* 畫面內移動 */
--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);   /* iOS 風格抽屜,出自 Ionic */

還有一句很絕對的話:UI 動畫不要用 ease-in。理由是 ease-in 一開始動得慢,而開頭正是使用者盯得最緊的時候,同樣 300ms,ease-in 感覺比 ease-out 慢。

要多快?

元件時長
按鈕按壓回饋100–160ms
tooltip、小 popover125–200ms
dropdown、select150–250ms
modal、drawer200–500ms
總原則是 UI 動畫控制在 300ms 內。它把速度和「體感效能」綁在一起講:轉得快的 spinner 讓載入感覺比較快,即使實際時間一樣。另一條我覺得很實用的是進出不對稱:使用者在做決定的地方可以慢(例如長按刪除 2 秒 linear),系統在回應的地方要快(放開時 200ms ease-out)。

細節清單

最後的 Review Checklist 有 11 列,我挑幾條最容易在 AI 產出裡看到的:

  • transition: all 要改成指定屬性。
  • 進場不要從 scale(0) 開始,改成 scale(0.95) 加 opacity: 0。
  • popover 的 transform-origin 要對準觸發點;modal 例外,維持置中。
  • hover 動畫要包在 @media (hover: hover) and (pointer: fine) 裡,避免觸控裝置點一下就觸發。
  • 進場和離場同速要改成離場比較快。
  • 一起出現的多個元素加 30–80ms 的錯開。
無障礙那一節的立場也值得記:減少動態是「更少、更溫和」,不是全部歸零,透明度和顏色變化要留著,拿掉的是位移。

🧪 實測:同一個下拉選單,有沒有用上它差在哪

我開了幾個乾淨的資料夾,Claude Code 版本 2.1.306、模型 Sonnet,用 claude -p 跑同一句需求,排除我自己的全域 CLAUDE.md 與 rules,避免它們干擾:

在目前資料夾建立單一檔案 dropdown.html:一個「更多操作」下拉選單(按鈕點開,
裡面 5 個選項),要有開啟與關閉的動畫,點外面或按 Esc 會關閉,選項可以用
鍵盤上下鍵移動。只用原生 HTML/CSS/JS,不要裝套件、不要用 CDN。
寫完檔案就結束,不用解釋。

對照組沒裝 skill,跑了 3 次;實驗組在需求前面加 /emil-design-eng。另外有 3 次是裝了 skill、但不點名(下一節談觸發)。

程式碼差在哪

項目沒用 skill(3 次)點名 /emil-design-eng
曲線內建 ease--ease-out: cubic-bezier(0.23, 1, 0.32, 1)
開/關時長160ms/160ms,對稱170ms/110ms,關閉較快
起始狀態scale(.96) + translateY(-6px)scale(0.95) + translateY(-4px)
按鈕按下3 次中有 2 次加了 scale(.97)scale(0.97)
減少動態transition: none、0.01s 或 0s,全部歸零只拿掉 transform,保留透明度淡入淡出
檔案長度163/163/147 行232 行
點名版的 CSS 註解直接寫著「exit: faster than enter」:
.menu {
  transform-origin: top left;
  opacity: 0;
  transform: scale(0.95) translateY(-4px);
  /* exit: faster than enter */
  transition: opacity 110ms var(--ease-out), transform 110ms var(--ease-out),
    visibility 0s linear 110ms;
}
.menu[data-open="true"] {
  opacity: 1;
  transform: none;
  transition: opacity 170ms var(--ease-out), transform 170ms var(--ease-out),
    visibility 0s;
}

這幾項都能對回 skill 原文:自訂 ease-out、dropdown 150–250ms、不從 scale(0) 開始、按鈕 scale(0.97)、離場比進場快、減少動態保留透明度。

用 Playwright 量動態

開啟後的靜態畫面,兩邊幾乎分不出來。差異只有 icon 換成 SVG、刪除前面加了分隔線:

沒用 skill 與點名 emil-design-eng 的下拉選單開啟畫面並排,版面與選項幾乎相同,只差在 icon 樣式與分隔線

所以我改量動態。用 Playwright 開 headless Chromium,在每個 requestAnimationFrame 讀選單的 computed opacity 與 transform,開啟量一次、按 Esc 關閉量一次:

版本opacity 首次 ≥ 0.9關閉時 opacity ≤ 0.1完全關閉
沒用 skill #1183ms106ms160ms
沒用 skill #2154ms114ms169ms
沒用 skill #3152ms100ms155ms
點名 /emil-design-eng102ms50ms98ms
下拉選單透明度曲線:點名 emil-design-eng 的版本開啟約 100ms 就接近不透明,關閉約 50ms 就接近透明;沒用 skill 的版本兩個方向都比較慢

點名版的總時長只比對照組多 10ms,但強 ease-out 讓大部分變化擠在前段,看起來「一按就出來」。關閉方向的差距更大,是因為時長本身就縮短了。headless 的 rAF 取樣有幾毫秒抖動,這些數字適合拿來比相對快慢,不適合當絕對規格。

我也開了 reducedMotion: 'reduce' 再量一次:點名版的 scale 全程停在 1,透明度照樣漸變;對照組因為整段 transition 被關掉,畫面是直接跳出來。

🔍 它會自動觸發嗎?要吃多少 context?

觸發:3 次只有 1 次

Claude Code 在 session 開始時會把每支 skill 的 description 列給模型,由模型自己決定要不要載入。emil-design-eng 沒有 disable-model-invocation,所以理論上可以自動觸發。同 repo 只有 pick-ui-library、prototype、review-animations 三支設了這個欄位,必須手動點名。

但它的 description 只說「這份 skill 編碼了 Emil 的哲學」,沒有像 apple-design 那樣列出「Use when building or reviewing…」的情境清單。我裝好 skill、不點名,同一句需求跑了 3 次:

次數有沒有呼叫 Skill 工具產出的曲線與時長
第 1 次沒有transform 用了 cubic-bezier(0.23, 1, 0.32, 1),opacity 仍是 ease,開關都約 150–180ms
第 2 次沒有開啟用帶回彈的 cubic-bezier(.2,.9,.3,1.1),180ms 開、150ms 關
第 3 次有自訂 ease-out、180ms 開/120ms 關,另外把 hover 包進 (hover: hover) and (pointer: fine)
三次的 stream-json 初始化清單都有列出 emil-design-eng,所以不是沒裝好。第 1 次沒載入 skill 卻用了同一條曲線,可能是 Sonnet 本身就認得這條常見的強 ease-out,也可能是看到 skill 清單受到影響;只有一次樣本,我不下結論。3 次也不能說觸發率就是三分之一,只能說「裝了不等於會用」。

Context 成本:約 9.9k tokens

SKILL.md 是 27,797 bytes。我從 stream-json 讀每次執行第一個請求的 input tokens:

情境第一個請求 input tokens單次執行成本
沒裝 skill25,3600.076 美元
裝了沒點名、沒觸發25,4190.075 美元
點名 /emil-design-eng35,3130.140 美元
只是「裝著」的代價很小,description 大約佔 59 tokens。一旦載入,就是一次多將近 1 萬 tokens,成本是 claude -p 回報的 total_cost_usd。自動觸發的第 3 次也類似,成本 0.124 美元、對話輪數從 2 輪變 4 輪。如果你在一個 session 裡只是要改一個按鈕顏色,載入這份 skill 不划算。

⚖️ 跟 apple-design 差在哪、哪裡重疊?

同 repo 的 apple-design 是 290 行,我之前實測過。兩支並排讀完:

比較emil-design-engapple-design
行數674290
出發點Emil 自己做 sonner、vaul 與產品 UI 的判斷Apple WWDC 設計演講,主軸 Designing Fluid Interfaces
主力題材一般 UI 元件:按鈕、dropdown、popover、tooltip、toast手勢互動:拖曳、sheet、動量、橡皮筋、半透明材質
時間觀以 duration 與 easing 為主,spring 是其中一節以 spring 為主,主張用行為取代固定時長
獨有內容頻率表、Before/After/Why 審查格式、clip-path 技法、Sonner 原則動量投射公式、橡皮筋公式、材質層級、字體光學尺寸
description一句話,沒寫使用情境列出 drag、spring、sheet 等使用情境
重疊的地方有四個:可中斷性(兩邊都說 transition 或 spring 比 keyframes 好接手)、元素從觸發點展開、減少動態不等於沒有回饋、spring 用 Apple 式的 duration 加 bounce 設定。兩邊在這幾點的說法一致,沒有互相打架。

我的選法:要寫的是拖曳、可甩動的面板,用 apple-design;是一般網頁元件的開關動畫、要審查既有 CSS 的時長與曲線,用 emil-design-eng。兩支都裝不會撞名,但同一次需求兩支都載入會再多吃一份 context。

⚙️ 怎麼安裝與點名?

只裝這一支,指令是:

npx skills@latest add emilkowalski/skills --skill emil-design-eng

不加參數時 CLI 會問要裝給哪個 agent。我實際跑的是帶 agent 與跳過確認的版本,當時 skills CLI 是 1.7.4:

npx -y skills@latest add emilkowalski/skills --skill emil-design-eng -a claude-code -y

跑完專案裡會出現 .claude/skills/emil-design-eng/SKILL.md(674 行)和 skills-lock.json。CLI 會顯示第三方安全評估,我那次是 Gen Safe、Socket 0 alerts、Snyk Low Risk。

點名方式有兩種:

  • 在需求最前面打 /emil-design-eng,後面接具體需求。這樣 skill 內容會直接展開進對話。
  • 在需求裡寫「依照 emil-design-eng skill」,讓模型自己去載入。
單獨打 /emil-design-eng 不帶問題時,skill 規定只回一句「I'm ready to help you build interfaces that feel right…」,不會主動給建議。這是設計,不是壞掉;要它做事就把需求接在同一行。

要它審查既有程式碼時,輸出會是 Before/After/Why 三欄表格,這是 skill 標為 Required 的格式。

🚧 什麼情況不適合用

  • 只改小東西:一次載入約 9.9k tokens。改一個顏色或文案不需要它。
  • 手勢為主的互動:它有 Gesture and Drag 一節,但深度不如 apple-design 的動量與橡皮筋公式。
  • 鍵盤密集的工具型產品:它本身就會建議你少做動畫。如果你期待它「幫我加很多動畫」,結果可能相反。
  • React Native、Swift:範例以 CSS、Motion 為主。同 repo 有 animate-expo 和 write-swift。
  • 想用靜態截圖驗收:這次實測靜態畫面幾乎一樣,要看差異得量時間曲線,或至少在 DevTools 的 Animations 面板放慢播放。

🔗 延伸資源

下一步建議:挑你專案裡一個 dropdown 或 popover,用 /emil-design-eng 請 Claude Code 依 Review Checklist 審一次,拿到 Before/After/Why 表格後,先只改 easing 和離場時長兩項,再用 DevTools 放慢 4 倍看前後差異。
author
陳
陳彥彤

AI 工程師 · AI 顧問。Java 後端 8 年、AI 工程師 2 年。AI 內訓 · AI 導入顧問 · 前後端與雲端培訓。

support

覺得文章有用可以到 GitHub 給個 star,或是透過信箱聊聊 AI 內訓、AI 導入顧問或前後端 / 雲端培訓。