微互動與動畫過渡
微互動不是「讓頁面動起來」;它是使用者操作後收到的回應。按鈕是否已經被按下?資料是否正在儲存?錯誤要如何恢復?如果動畫沒有回答這些問題,就只是噪音。
一個互動的四個部分
以「儲存設定」為例:
| 部分 | 設計問題 | 具體結果 |
|---|---|---|
| Trigger | 什麼會開始互動? | 使用者按下儲存 |
| Feedback | 使用者需要知道什麼? | 按鈕進入儲存中 |
| State | 中途與完成後怎麼看? | loading、success、error |
| Recovery | 失敗後能做什麼? | 保留輸入並提供重試 |
這份表可以直接成為 AI 實作元件時的提示上下文。
用 CSS 實作按鈕回饋
<button class="button" type="submit">
<span class="button__label">儲存設定</span>
<span class="button__spinner" aria-hidden="true"></span>
</button>
.button {
transition:
background-color 160ms ease,
box-shadow 160ms ease,
transform 160ms ease;
}
.button:hover {
box-shadow: 0 4px 12px rgb(37 99 235 / 0.24);
}
.button:active {
transform: translateY(1px);
}
.button[data-loading='true'] .button__label {
opacity: 0.7;
}
不要使用 transition: all。它會讓尺寸、位置和其他未預期改變的屬性也加入動畫,造成 layout shift 和難以測試的行為。
loading 不是 success
async function saveSettings(form, button) {
button.dataset.loading = 'true';
button.disabled = true;
try {
await save(form);
button.dataset.loading = 'false';
button.dataset.status = 'success';
} catch (error) {
button.dataset.loading = 'false';
button.disabled = false;
showError('儲存失敗,請檢查網路後重試。');
}
}
動畫只能補充狀態,不能代替文字、ARIA 或可操作的錯誤訊息。
尊重減少動態偏好
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
課後練習
為一個搜尋框設計 idle、typing、loading、empty 和 error 五種狀態。先用文字列出狀態,再決定哪些地方需要動畫,最後用鍵盤和 prefers-reduced-motion 測試。
下一課會把這些互動規則與語意 HTML、鍵盤操作和錯誤訊息結合。