前端設計

emil-design-eng

以設計工程觀點審查互動細節,強調可中斷動畫、正確 transform origin、精準 easing、觸控回饋與無障礙,並用表格提出前後修正。

查看來源 ↗
作者/來源emilkowalski★ 37,788 GitHub Stars
測試版本d23d7f88a2e2更新日期:2026-08-21T09:32:32Z
支援平台OpenAI · Claude本次實測平台
安裝方式npx skills add emilkowalski/skills --skill emil-design-eng
公平比較方式

三個問題,分別測載入前與載入後。

每次回答都在全新環境中產生。兩邊使用相同模型與設定,唯一差別是右側載入了這個 Skill。

模型版本gpt-5.6-sol帳號方案ChatGPT Plus思考強度medium執行工具codex-cli-native-skill
真實測試使用本機已登入的 ChatGPT Plus 實際執行,完成於 2026年9月3日。所有檢查通過後才公開,回答未經人工改寫。
三階段測試3 個問題全部完成版本核對3/3 通過環境清理9/9 通過測試隔離每回合全新環境
01
基礎題

審查下拉選單動畫

測試問題

審查下拉選單:transition: all 300ms ease-in;關閉時 scale(0);transform-origin 永遠 center;按鈕沒有 active 狀態。請用 Markdown 表格提供 Before、After、Why,並給一段修正後 CSS。

載入前 · 0 個 Skill載入後 · 版本已核對再次確認 · 0 個 Skill已確認模型套用 Skill三個環境皆已清理
未載入 Skill506 tokens · 16217 ms
| 項目 | Before | After | Why | |---|---|---|---| | Transition | `transition: all 300ms ease-in` | 僅 transition `opacity`、`transform`,開啟用 `ease-out`、關閉用較短的 `ease-in` | 避免非預期屬性產生動畫;開啟俐落、關閉迅速 | | 關閉縮放 | `scale(0)` | `scale(0.96)` | 避免選單坍縮成一點,減少突兀感 | | Transform origin | 永遠 `center` | 依選單位置設為觸發按鈕方向,例如 `top right` | 動畫看起來從觸發來源展開 | | 按鈕狀態 | 沒有 active 狀態 | 加入 `:active`,並用 `aria-expanded="true"` 表示選單已開啟 | 提供即時操作回饋與清楚的持續狀態 | ```css .dropdown-trigger { transition: background-color 120ms ease, transform 120ms ease; } .dropdown-trigger:hover { background-color: var(--button-hover-bg); } .dropdown-trigger:active { transform: scale(0.97); } .dropdown-trigger[aria-expanded="true"] { background-color: var(--button-active-bg); } .dropdown-menu { opacity: 1; transform: scale(1); transform-origin: top right; transition: opacity 180ms ease-out, transform 180ms ease-out; } .dropdown-menu[data-state="closed"] { opacity: 0; transform: scale(0.96); pointer-events: none; transition: opacity 120ms ease-in, transform 120ms ease-in; } @media (prefers-reduced-motion: reduce) { .dropdown-trigger, .dropdown-menu { transition: none; } } ```
已載入 Skill +1026 tokens · 37714 ms
| Before | After | Why | | --- | --- | --- | | `transition: all 300ms ease-in` | `transition: opacity 180ms var(--ease-out), transform 180ms var(--ease-out)` | 只動畫 `opacity` 與 `transform`;較短的自訂 ease-out 能提供即時回饋。 | | 關閉時 `transform: scale(0)` | `opacity: 0; transform: scale(0.95)` | 避免選單像從虛無中消失,保留自然的視覺形體。 | | `transform-origin: center` | `transform-origin: var(--transform-origin)` | 下拉選單應從觸發按鈕的位置展開,而非固定從中心縮放。 | | 按鈕沒有 `:active` 狀態 | `transform: scale(0.97)` | 提供清楚、即時且細微的按壓回饋。 | ```css :root { --ease-out: cubic-bezier(0.23, 1, 0.32, 1); } .dropdown { opacity: 1; transform: scale(1); transform-origin: var(--transform-origin); transition: opacity 180ms var(--ease-out), transform 180ms var(--ease-out); } .dropdown[data-state="closed"] { opacity: 0; transform: scale(0.95); pointer-events: none; } .dropdown-trigger { transition: transform 120ms var(--ease-out); } .dropdown-trigger:active { transform: scale(0.97); } @media (prefers-reduced-motion: reduce) { .dropdown, .dropdown-trigger { transition-property: opacity; } .dropdown[data-state="closed"], .dropdown-trigger:active { transform: none; } } ```
02
實務題

設計可中斷通知動畫

測試問題

通知 toast 會每 2 秒收到新項目;目前每次都重播 600ms 彈跳,快速操作時排隊且無法中斷。請提出狀態與動畫策略、duration/easing、進出場、重新排序、reduced-motion,並說明如何避免動畫阻礙操作。

載入前 · 0 個 Skill載入後 · 版本已核對再次確認 · 0 個 Skill已確認模型套用 Skill三個環境皆已清理
未載入 Skill1004 tokens · 24140 ms
### 狀態與動畫策略 每則 toast 採獨立狀態: `entering → visible → exiting → removed` - 新項目只動畫新增的 toast;既有項目不重播。 - 同一 toast 更新內容時原地更新,不重新進場。 - 動畫必須可中斷:新狀態直接從當前視覺位置轉向目標狀態,不等待前一段動畫完成。 - 不建立動畫佇列;新項目到達時立即重新計算最終排列。 - 設定同時顯示上限(建議 3–5 則);超出時合併為「另有 N 則通知」或將最舊項目移入通知中心。 ### 動畫規格 | 情境 | Duration | Easing | 效果 | |---|---:|---|---| | 進場 | 180–220ms | `cubic-bezier(.2,.8,.2,1)` | `opacity: 0 → 1`,由外側平移 12–16px;不使用彈跳 | | 出場 | 120–160ms | `cubic-bezier(.4,0,1,1)` | 淡出並向外平移 8px | | 重新排序 | 160–200ms | `cubic-bezier(.2,0,0,1)` | FLIP/transform 位移到新位置 | | hover/focus 回饋 | 80–120ms | `ease-out` | 僅顏色或陰影變化 | 進場不應使用 overshoot、spring 或反覆縮放;每 2 秒新增一次時,600ms 彈跳會持續搶奪注意力。若需要層次感,可使用極輕微的 `scale(.98) → scale(1)`,但位移與透明度已足夠。 ### 排列與重新排序 - 新 toast 固定加入靠近錨點的一端,例如右上角時加入頂端,其餘項目向下移。 - 使用 FLIP 或只動畫 `transform`,避免逐項觸發 layout animation。 - 刪除某則時,先讓該則淡出;其餘項目同步移向最終位置,不逐個延遲。 - 若新項目在重新排序途中抵達,取消目前 transition,讀取當前 transform,直接動畫到最新位置。 - 不使用 stagger;通知頻繁時 stagger 會製造佇列與延遲。 ### Reduced motion 在 `prefers-reduced-motion: reduce` 下: - 停用平移、縮放、彈跳與重新排序動畫。 - 進出場僅使用 60–100ms opacity,或直接切換。 - 項目重新排序立即完成。 - 不以動畫作為「新通知」的唯一提示;保留文字、圖示及適當的無障礙公告。 ### 避免阻礙操作 - toast 出現後立即可點擊或關閉,不等待動畫結束。 - 動畫層本身不攔截輸入;僅 toast 卡片與其控制項使用 `pointer-events: auto`。 - 點擊、關閉、hover 或鍵盤 focus 時,立即中斷位移動畫並固定目前項目;自動消失計時應暫停。 - 使用者正在操作某則 toast 時,不重新排列該則;新通知可加入其他位置,待操作結束再一次對齊。 - 關閉按鈕位置保持穩定,觸控目標至少 44×44px。 - 不因新 toast 搶走焦點;以 `role="status"`/`aria-live="polite"` 宣告一般通知,只有真正緊急事件才使用 assertive。 - 自動消失時間從進場完成後計算,建議至少 5–8 秒;含操作或較長文字的通知不應自動消失。 - 動畫只使用 `transform` 與 `opacity`,避免改動 `top`、`height` 等造成卡頓與點擊目標漂移。
已載入 Skill +1944 tokens · 51731 ms
## Toast 狀態與動畫策略 | 情境 | 策略 | | --- | --- | | 新 toast 到達 | 立即加入可見堆疊,只替新項目播放進場;既有項目不重播 | | 相同 ID 更新 | 原地更新內容,不重新進場;必要時僅淡化內容 `100–150ms` | | 堆疊重新排序 | 使用 FLIP/layout transition,從目前視覺位置平滑移到新位置 | | 動畫途中再次更新 | 取消舊目標並從當前位置重新計算;不得等待前一段動畫結束 | | 手動關閉/逾時 | 進入 `exiting`,視覺退場後移除;業務操作立即生效 | | 超過顯示上限 | 建議最多顯示 3–5 則;最舊項目直接退場,不建立動畫佇列 | 狀態可採: ```text entering → visible → exiting → removed ↑ content update ``` ### 動畫規格 - 進場:`opacity: 0 → 1`,並從靠近螢幕邊緣的方向位移 `12–16px → 0`。 - Duration:`180–220ms`。 - Easing:`cubic-bezier(0.23, 1, 0.32, 1)`,快速起步、柔和停止。 - 不使用 `600ms` 彈跳、`scale(0)` 或明顯 overshoot;高頻通知會讓這些效果顯得拖沓。 - 退場:沿進場方向返回,`opacity: 1 → 0`,位移約 `8–12px`。 - Duration:`120–160ms`。 - Easing:同一組強 ease-out。 - 退場應比進場快,讓使用者感受到系統已立即回應。 - 重新排序:只動畫 `transform`,不要動畫 `top`、`height` 或 `margin`。 - Duration:`160–200ms`。 - Easing:`cubic-bezier(0.77, 0, 0.175, 1)`。 - 新通知到達時,既有 toast 僅移動到新位置,不改變 opacity、不縮放。 - 若重排尚未完成又有新項目,從當前畫面位置重新定向,不串接或排隊。 實作上優先使用可中斷的 CSS transition;若使用 WAAPI,更新前先 `cancel()`,保留當前 computed transform 後再設定新目標。避免動態 toast 使用會從頭重播的 keyframes。 ```css .toast { transition: transform 180ms cubic-bezier(0.77, 0, 0.175, 1), opacity 140ms cubic-bezier(0.23, 1, 0.32, 1); will-change: transform, opacity; } ``` ### Reduced motion ```css @media (prefers-reduced-motion: reduce) { .toast { transform: none !important; transition: opacity 120ms ease-out; } } ``` 保留短暫淡入淡出以說明狀態變化,但取消位移、縮放、彈跳與堆疊滑動;重新排序直接完成。 ### 避免動畫阻礙操作 - Toast 一加入 DOM 就可點擊,不等待進場完成。 - 關閉、復原或其他 action 在點擊當下執行;退場只是結果呈現。 - 動畫僅使用 `transform` 與 `opacity`,避免 layout/paint 抖動。 - 動畫層不得覆蓋其他控制項;容器空白區使用 `pointer-events: none`,toast 本身恢復 `pointer-events: auto`。 - Hover、鍵盤 focus、拖曳與頁籤隱藏時暫停自動關閉計時,避免使用者操作途中消失。 - 動畫期間維持可見焦點;被 focus 的 toast 不因堆疊上限自動移除。 - 快速連續操作時直接更新最終狀態,不因動畫建立工作佇列。 - 若通知流量持續升高,可將重複項目聚合成「新增 4 則通知」,降低視覺干擾。
03
壓力題

區分 hover 與觸控回饋

測試問題

一個卡片只靠 hover 顯示操作,手機沒有回饋;所有圖示同時旋轉 360 度;modal 從觸發按鈕位置縮放。請用指定表格格式審查,分別給滑鼠、鍵盤、觸控與 reduced-motion 的修正。

載入前 · 0 個 Skill載入後 · 版本已核對再次確認 · 0 個 Skill已確認模型套用 Skill三個環境皆已清理
未載入 Skill847 tokens · 20424 ms
| 問題 | 滑鼠修正 | 鍵盤修正 | 觸控修正 | `reduced-motion` 修正 | |---|---|---|---|---| | 卡片操作只在 `hover` 顯示 | 可保留 hover 揭露,但卡片或操作區移入時維持穩定,避免游標移動造成閃爍。 | 操作必須可 Tab 聚焦,並在 `:focus-within` 時顯示;使用真正的 `button`/連結,焦點樣式清楚。 | 不依賴 hover;操作預設常駐,或第一次點卡片展開操作、第二次才執行,並提供按下狀態回饋。 | 顯示/隱藏改為立即切換或短暫淡入淡出,不使用位移、縮放。 | | 所有圖示同時旋轉 360° | 僅讓目前 hover 的圖示提供低幅度回饋;避免整組旋轉。 | 僅讓目前聚焦或啟用的圖示變化,且不可因取得焦點自動播放完整旋轉。 | 僅被點擊的圖示提供短促 `pressed` 回饋;不要用旋轉代替操作結果或載入狀態。 | 在 `prefers-reduced-motion: reduce` 下移除旋轉,以顏色、背景、描邊或靜態狀態替代。 | | Modal 從觸發按鈕位置縮放 | 若需保留空間關聯,可從按鈕附近淡入,但避免大幅縮放;遮罩同步出現。 | 開啟後把焦點移至 modal,限制焦點於其中;關閉後還原至觸發按鈕。動畫來源不可影響焦點順序。 | 改由畫面中央淡入,或依元件型態由底部滑入;避免從手指遮住的按鈕位置縮放。 | 停用縮放與長距離位移,直接顯示或使用極短淡入;關閉亦同。 |
已載入 Skill +1387 tokens · 41795 ms
| Before | After | Why | | --- | --- | --- | | **滑鼠:**卡片操作只在未限制的 `:hover` 顯示;所有圖示一起旋轉 360°;modal 從按鈕位置縮放 | 僅在 `@media (hover: hover) and (pointer: fine)` 中顯示 hover 操作;只讓目前操作的圖示以顏色或 `scale(0.97)` 提供 100–160ms 回饋;modal 從畫面中央以 `opacity: 0; scale(0.95)`、200–250ms ease-out 進場 | Hover 應只套用於精準指標;大量旋轉分散注意力;modal 不屬於觸發器的空間延伸,應維持中央原點 | | **鍵盤:**Tab 無法顯示卡片操作;啟用一項操作仍觸發全部圖示旋轉;modal 動畫從觸發按鈕展開 | 以 `:focus-within` 顯示操作並提供清楚的 `:focus-visible`;支援 Enter/Space;鍵盤觸發時取消圖示與 modal 位移/縮放動畫;開啟後聚焦 modal、關閉後還原至觸發按鈕 | 鍵盤使用者必須能發現、操作並追蹤焦點;高頻鍵盤操作應立即回應,不應被動畫拖慢 | | **觸控:**沒有 hover,因此操作不可見且點按缺乏回饋;點擊造成所有圖示旋轉 | 將主要操作常駐,或提供始終可見且有標籤的更多操作按鈕;加入 `:active { transform: scale(0.97) }`;只更新被點擊項目的狀態;modal 維持中央淡入/輕微縮放 | 觸控介面不能依賴 hover;按壓回饋應即時且局部,避免誤以為多個操作同時執行 | | **Reduced motion:**仍旋轉圖示 360°,modal 仍沿觸發位置移動並縮放 | 在 `prefers-reduced-motion: reduce` 下移除 `rotate`、位移與縮放;以 150–200ms 的 opacity/color transition 顯示狀態與 modal | 保留必要的狀態辨識,同時避免可能造成不適的旋轉與空間移動 |
測試紀錄

這次測試記錄了什麼?

固定不變兩邊使用相同的模型版本、帳號方案、系統指令、思考強度與測試問題。

分開測試每次回答都使用全新環境。載入前不含任何 Skill,載入後只含本頁指定的 Skill。

完整才公開三題的所有步驟與環境清理都成功後,才顯示結果。若無法確認模型是否套用 Skill,也會清楚標示。