Communitygithub.com

grahama1970/apple-design

Apple 的介面設計方法論與流暢、物理性的動態,轉化為 Web 實作。適用於建立或審查手勢驅動的 UI、彈簧動畫、拖曳/滑動/面板互動、動量與可中斷的過渡、半透明材質與深度、排版(光學尺寸、字距、行距)、減少動畫模式,以及 Apple 風格介面背後的設計基礎(回饋、空間一致性、克制)。

apple-design 是什麼?

這個技能將 Apple 在 WWDC 2018《設計流暢介面》中所提出的設計哲學與實作模式,轉化為 Web 開發者可執行的原則與程式碼範例。核心思想是:當介面的動畫從螢幕上的當前值開始、繼承使用者的速度、向前投射動量,並且能夠在任何瞬間被抓住並反向時,介面就會感覺有生命力。彈簧動畫是實現這一切的關鍵工具,因為它們本質上可中斷且能感知速度。 技能涵蓋了 14 個具體領域,從「回應延遲」、「直接操作」、「可中斷性」這些基本原則,到「動量投射」、「橡膠邊界」、「材質與深度」等進階技術。每個原則都附有 Web 平台的實作指南,包括 CSS 和 JavaScript 範例(Pointer Events、requestAnimationFrame、Motion 函式庫)。 這個技能適合使用 Claude Code、Cursor 或 Codex 等 AI 代理工具的開發者,特別是在建立手勢驅動的 UI 元件(如底部面板、輪播、拖曳排序)或進行設計審查時。它提供了一個完整的檢查清單,確保動畫的每個細節——從按鍵瞬間的回饋到釋放手勢後的減速——都符合 Apple 對於流暢、直接且人性化互動的嚴格標準。

相容平台~Claude Code~Codex CLI~Cursor✓Antigravity
npx skills add https://github.com/grahama1970/agent-skills/tree/main/skills/apple-design

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Apple 設計

Apple 如何打造那些不再像電腦、而更像你自身延伸的介面。這份知識來自 Apple 的 WWDC 設計講座——主要是《設計流暢介面》(WWDC 2018)——並將其提煉和轉化到 Web 平台(CSS、Pointer Events、requestAnimationFrame、像 Motion/Framer Motion 這類的彈簧函式庫)。

核心主線:當動畫從螢幕上的當前值開始、繼承使用者的速度、向前投射動量,並且能夠在任何瞬間被抓住並反向時,介面就會感覺有生命力。 彈簧是讓這一切變得自然自如的工具,因為它們本質上可中斷且能感知速度。

核心概念

「當我們讓介面與我們的思考和移動方式保持一致時,神奇的事情就會發生——它不再感覺像一台電腦,而開始感覺像我們自身天衣無縫的延伸。」

一個介面是流暢的,當它像物理世界一樣運作:事物立即回應、連續移動、攜帶動量、在邊界處產生阻力,並且可以在運動途中被重新導向。以下所有的原則都是為了更接近這個目標。

Apple 將設計框架定義為服務四種人類需求:安全感/可預測性、理解、成就感、以及愉悅感。 這裡的每一條規則都服務於其中一項需求。

1. 回應——消除延遲

當延遲出現的那一刻,直接操作的感覺就會「從懸崖邊墜落」。回應是所有其他環節的基礎。

  • 在指標按下時回應,而不是在釋放時。 按鈕在按下的瞬間就應該被高亮。等待 click/觸摸抬起才顯示回饋,感覺就像死了一樣。
  • 警惕每一毫秒的延遲。 審查所有防抖動 (debounce)、人為計時器、過渡等待,以及約 300 毫秒的點擊延遲。輸入路徑上任何非必要的東西都是退步。
  • 回饋必須在互動過程中持續存在,而不只是在結束時。 對於拖曳、滑桿或抽屜,整個過程中 UI 必須與指標 1:1 地同步更新——永遠不要在手勢完成時才動畫。
/* 回饋存在於按下的瞬間,並且是即時的 */
.button:active {
  transform: scale(0.97);
  transition: transform 100ms ease-out;
}

2. 直接操作——1:1 追蹤

「觸摸和內容應該一起移動。」

當使用者拖曳某個東西時,它必須緊緊跟隨手指——並且尊重從他們抓住它的位置開始的偏移量。在抓住時立即對齊到元素中心,會立刻破壞這個幻覺。

  • 使用 Pointer Events 搭配 setPointerCapture,這樣即使指標移出元素的邊界,追蹤仍然繼續。
  • 追蹤一個短暫的速度/位置歷史記錄(最近幾次的 pointermove 事件),而不僅僅是目前的位置——你在釋放時會需要速度。
el.addEventListener('pointerdown', (e) => {
  el.setPointerCapture(e.pointerId);
  const grabOffset = e.clientY - el.getBoundingClientRect().top; // 尊重他們抓住的位置
  // ...追蹤位置 + 時間戳記歷史記錄以計算速度
});

3. 可中斷性——最重要的單一原則

「想法和手勢是同時發生的。」

每個動畫都必須在任何時刻被中斷和重新導向。使用者必須能夠在飛行中抓住一個移動中的元素並將其反向,而不需要等待動畫結束。一個正在關閉的模態視窗,如果使用者再次抓住它,應該要跟隨手指——而不是先完成關閉,然後再重新打開。

  • 永遠不要在過渡期間鎖定輸入。
  • 總是從當前顯示(目前)的值開始動畫,永遠不要從目標值開始。 在中斷時,讀取元素在螢幕上的即時變形 (transform),並從那裡開始新的動畫。從邏輯/目標值開始會導致明顯的跳躍。
  • 避免使用 CSS transitions 和 @keyframes 處理任何手勢驅動的動畫——它們無法在飛行中被順暢地抓取和反向。彈簧預設從當前值開始動畫,這正是中斷所需的。
  • 當手勢反向時,要混合速度——不要硬切。 在反向點用一個動畫取代另一個動畫會產生速度不連續性,就像撞上一堵「磚牆」。能夠在重新定位目標時攜帶速度的彈簧函式庫可以避免這個問題。(這就是 iOS 的加法動畫原生做的事;在 Web 上,選擇一個能夠從當前速度重新定位目標的彈簧函式庫。)
  • 將二維運動分解為獨立的 X 和 Y 彈簧。 在二維距離上使用單一彈簧,當 X 和 Y 具有不同速度時會失去同步。

4. 行為勝於動畫——使用彈簧

「把動畫想成是你和物體之間的對話,而不是由介面預先編寫好的腳本。」

預先編寫好、固定持續時間的動畫無法回應新的輸入。彈簧可以——新的輸入只是改變了目標,而運動保持連續。任何使用者可以觸摸的東西都應該使用彈簧。

Apple 刻意用兩個設計師友善的參數取代了物理三元素(質量/剛度/阻尼)。用這兩個參數思考:

  • 阻尼比 (Damping ratio)——控制過衝 (overshoot)。1.0 = 臨界阻尼,無彈跳,平穩停止。< 1.0 = 過衝並擺動。數值越低越彈。
  • 響應時間 (Response)——值到達目標的速度,單位是秒。數值越低反應越快。這不是「持續時間」——彈簧沒有固定的持續時間;它的穩定時間是從參數中浮現出來的。

預設值:

  • 大多數 UI 從阻尼 1.0(臨界阻尼)開始——優雅且不分散注意力。
  • 只有在手勢本身帶有動量時(一個輕彈、一個拋擲、一個拖曳釋放)才添加彈跳(阻尼約 0.8)。一個剛剛淡入的功能表如果過衝感覺很奇怪;但一個被你輕彈過的卡片過衝則感覺恰到好處。

Apple 實際出貨的具體數值:

互動阻尼響應時間
移動/重新定位(例如子母畫面)1.00.4
旋轉0.80.4
抽屜 / 面板0.80.3

Web 對應(Motion / Framer Motion):bounce + duration 的 spring API 與 Apple 的 damping + response 非常接近。一個安全的預設樣式是到處都使用 damping: 1.0 的彈簧;保留彈跳給由動量驅動的、物理性的互動。

import { animate } from 'motion';

// 臨界阻尼預設(無過衝)
animate(el, { y: 0 }, { type: 'spring', bounce: 0, duration: 0.4 });

// 動量互動——稍微彈跳,只因為前面有輕彈動作
animate(el, { y: target }, { type: 'spring', bounce: 0.2, duration: 0.4 });

5. 速度交接——拖曳與動畫之間的接縫

當手勢結束時,動畫必須以手指的精確速度繼續,這樣拖曳和動畫之間就不會有明顯的接縫。這是讓「流暢」與「尚可」之間產生最大區別的細節。

將指標的釋放速度作為彈簧的初始速度傳遞。有些彈簧 API 需要相對速度——將其除以到目標的剩餘距離來標準化:

relativeVelocity = gestureVelocity / (targetValue − currentValue)

例如:元素在 y=50,目標 y=150(還有 100px 要走),手指移動速度 50px/s → 初始彈簧速度 = 50 / 100 = 0.5。Framer Motion / Motion 直接接受絕對的 px/s 速度(velocity 選項),所以你通常直接傳入原始數值。

6. 動量投射——動畫到手指將要去的地方

「用小輸入創造大輸出。」

不要從釋放點對齊到最近的邊界。使用速度來投射靜止位置——就像滾動減速一樣——然後對齊到離該投射點最近的目標。這就是讓一個輕彈感覺像是把元素丟出去的原因。

Apple 的精確投射函數(來自《設計流暢介面》的範例程式碼):

// decelerationRate ≈ 0.998 為一般滾動感受;0.99 為更乾脆
function project(initialVelocity /* px/s */, decelerationRate = 0.998) {
  return (initialVelocity / 1000) * decelerationRate / (1 - decelerationRate);
}

const projectedEndpoint = currentPosition + project(releaseVelocity);
const target = nearestSnapPoint(projectedEndpoint);   // 從投射結果選擇目標
animateSpringTo(target, { velocity: releaseVelocity }); // 然後交接速度(§5)

注意:物理課本中的 v²/(2·decel) 不是 Apple 使用的公式——請使用上面的指數衰減形式。這是優良底部面板和輪播(Vaul、Embla)中的標準行為。

7. 空間一致性——對稱路徑,錨定原點

「如果某個東西以一種方式消失,我們會預期它從它來的地方出現。」

  • 進入和退出應沿著相同路徑。 一個從右側滑入的面板必須向右側關閉。從右側進入 / 從底部離開會讓人感覺脫節和困惑。
  • 將互動錨定到它們的來源。 一個功能表、彈出視窗或面板應該從觸發它的元素處開始——將 transform-origin 設定到觸發器,這樣按鈕和內容之間的空間關係就很明顯。(這與彈出視窗從其觸發器縮放,而非其中心縮放,是相同的原點感知要點。)
  • 在可逆的過渡上鏡像緩動函數,使離開路徑與返回路徑匹配(對兩個方向使用相反的 cubic-bézier 控制點)。

8. 在手勢方向上的提示

人類從軌跡預測最終狀態。中間的動畫應該暗示事物將要往哪裡去——控制中心模組「朝著你的手指向上和向外生長」。讓過渡影格指向結果,而不僅僅是盲目地內插到它。

9. 橡膠邊界——軟邊界

在邊界處,要漸進地抵抗,而不是硬生生停止。硬停止讀起來像「凍結」;持續的阻力讀起來像「有回應,但這裡沒有更多東西了」。施加阻尼,隨著使用者拖曳超出邊界的距離增加而增加。

// 超出邊界越遠,元素跟隨得越少——真實世界的物體在停止前會減速
function rubberband(overshoot, dimension, constant = 0.55) {
  return (overshoot * dimension * constant) / (dimension + constant * Math.abs(overshoot));
}

10. 手勢設計細節(「感覺」檢查清單)

  • 點擊: 在觸摸-按下時立即高亮,在觸摸-抬起時提交。在目標周圍添加約 10px 的滯後/點擊緩衝區,並允許通過拖曳離開再回來的方式取消。
  • 拖曳/滑動: 在提交到某個方向之前,需要一個小的移動閾值(滯後,約 10px),然後 1:1 追蹤。
  • 從第一次移動就並行偵測所有合理的手勢,一旦意圖明確,就自信地取消失敗者。避免那些只報告最終狀態的辨識器(如 swipeleft 類型的事件)——它們會丟棄你製作回饋所需的連續追蹤。
  • 最小化辨識延遲。 雙擊偵測無可避免地會延遲單擊;只有在真正需要雙擊的地方才付出這個代價。

11. 影格級別的平滑度

平滑度關乎的是影格裡的內容,而不僅僅是影格率。

  • 保持每個影格的位置變化低於感知閾值,以避免閃爍感 (strobing)。
  • 對於非常快速的運動,微妙的動態模糊 / 拉伸可以編碼速度,並且比銳利的硬線條看起來更好。
  • requestAnimationFrame 是 Web 上與顯示器同步的時鐘(Apple 使用 CADisplayLink)。只對合成器友善的屬性進行動畫——transform 和 opacity——並在即將發生動畫的地方使用 will-change 提示。

12. 材質與深度——半透明傳達層級

Apple 使用半透明材質作為一個浮動的功能層,在不過度搶眼的情況下帶來結構感。在 Web 上,使用 backdrop-filter 近似。

  • 將導覽列/工具列/面板創建為半透明層(backdrop-filter: blur() + 半透明背景),內容在下方滾動——而不是佔據固定條帶的不透明條。
  • 材質重量編碼層級: 較深/較重的材質區分結構區域(側邊欄);較輕的材質吸引注意力到互動元素(按鈕)。永遠不要將一個淺色半透明表面疊在另一個上面——可讀性會崩潰。
  • 較大的表面應感覺較厚: 比較小的碎片有更強的模糊和更深的陰影。考慮情境感知的陰影——在繁忙/文字內容上使用較重的陰影以增加分離感,在純色背景上使用較輕的陰影。
  • 變暗以集中注意力,分開以保持流程。 一個模態任務將表面與變暗的遮罩配對,並將背景推後/向下。一個並行的、非阻塞的面板使用半透明和偏移而不使用遮罩,這樣流程就不會被中斷。對於堆疊的面板,逐漸變暗並將每個父層推後。
  • 生動效果 (Vibrancy) 使文字在變化的背景上保持可讀。 在模糊/半透明表面上,不要使用純灰色文字——使用更高對比度、稍重的字重和微小的字母間距提升。將顏色放在實心層上,而不是半透明的前景上。
  • 使用滾動邊緣效果,而不是硬分隔線。 取代在黏性標題下方使用 1px 邊框,在內容與浮動鍍鉻層交匯處淡出一個小的模糊/漸變遮罩——僅在浮動 UI 實際覆蓋內容的地方。
  • 物質化,而不只是淡入/淡出。 對於玻璃/模糊表面,在進入/退出時同時對模糊半徑和縮放進行動畫,這樣表面讀起來就像一個真實的材質到達,而不是一個普通的透明度淡入淡出。
.toolbar {
  background: rgba(255, 255, 255, 0.6);
  backdrop-filter: blur(20px) saturate(180%);
  border-top: 1px solid rgba(255, 255, 255, 0.4); /* 明亮的頂部邊緣 = 光線捕捉到材質 */
}

13. 多模態回饋——動畫 + 聲音 + 觸覺

結合感官的三個原則(來自《設計音頻-觸覺體驗》):

  1. 因果關係 (Causality)——回饋的原因必須顯而易見。在實際的因果事件(切換開關的瞬間、項目定位到位的瞬間)時觸發它,並使其特性與動作的物理性相匹配。
  2. 和諧 (Harmony)——視覺、聲音和觸覺必須在同一個影格觸發。它們之間的延遲會摧毀這個幻覺。不要讓 CSS 過渡落後於音頻/觸覺 (Vibration API)。
  3. 效用 (Utility)——只在有意義的地方添加回饋。將觸覺/聲音保留給重要的時刻(成功、錯誤、提交、到位)。過度回饋會訓練使用者忽略所有回饋。

14. 減少動畫與無障礙

減少動畫並不意味著沒有回饋

……

[因翻譯限制截斷]

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

grahama1970/acceptance-contract

Turn a client brief, zip bundle, directory, or single requirements file into a typed acceptance-contract bundle with extracted requirements, acceptance checks, open questions, an immutable-goal draft, and a create-report-backed decision report. Use when users say acceptance contract, brief to requirements, freeze the goal, create immutable goal, amend immutable goal, build a Battle requirements bundle, or extract requirements from this bundle.

grahama1970/agent-ecosystem

Canonical map and shared contracts for the agent-governance ecosystem: the pi.receipt_envelope.v1 boundary envelope, the component graph, and the rules for which component owns which schema. Use when wiring a skill or extension into the shared receipt world, when asking how shame, triage-error, tau, ask, project-watchdog, ops-herdr, ponytail, and Memory fit together, or when validating an envelope.

grahama1970/agentic-evals

Agentic evaluation of skills using multi-trial fixtures, deterministic command assertions, trajectory checks, safety constraints, and evidence-backed readiness scoring. Use when users ask for agentic evals, multi-trial skill evaluation, skill trajectory validation, or readiness scoring for a skill workflow.

grahama1970/agent-inbox

File-based inter-agent messaging with headless dispatch. Check inbox, send bugs/requests to other projects, automatically spawn headless agents to fix bugs, and track progress via task-monitor.

grahama1970/agents-registry

Generate and query the centralized agent identity registry. Scans .pi/agents/*/AGENTS.md, parses frontmatter, outputs agents-registry.json and optionally syncs to /memory for semantic search.

grahama1970/agent-status

Artifact-driven status surfaces for long-running project-agent work. Maintains status.json, events.jsonl, proof manifests, and a stale-aware STATUS.html so humans can tell where the agent is, what passed, what is still unproven, and what decision or action is next — without dashboard theater.

grahama1970/align

Round-based context alignment before execution. Use when the human, project agent, WebGPT, scillm, ask, dogpile, memory, or project-knowledge may each hold different facts about a task; especially before ambiguous design, infographic, product workflow, high-stakes implementation, plan-iterate, project-infographic, or multi-review work.

grahama1970/analytics

Flexible data science analytics for any dataset. Auto-discovers schema, recommends charts, exports to create-figure. Works with JSONL, JSON, CSV from any source.

grahama1970/analyze-chatterbox-emotions

Evaluate generated Chatterbox voice files as voice-quality artifacts: affect match, arousal/valence proxies, pause placement, intelligibility inputs, clipping, loudness, and discontinuity flags. Use when reviewing Chatterbox emotional tags, pauses, Turbo/base affect delivery, Persona Dream utterance renders, or whether generated speech matches an intended product-facing affect.

grahama1970/analyze-elf

Reverse-engineer features from ELF binaries. Extracts CLI commands, state machines, protocols, Zod schemas, and data models. Automatically generates a /create-walkthrough prosecution brief with Mermaid diagrams. Uses /treesitter for AST analysis of bundled JS/TS source.

grahama1970/animation-vocabulary

Reverse-lookup glossary that turns a vague description of a web animation or motion effect into its exact term ("the bouncy thing when a popover opens" → Pop in; "the iOS rubber-band scroll" → Rubber-banding). Use when the user asks "what's it called when…", or describes a motion effect without knowing its name and wants the right word to prompt an AI or designer with. For naming an effect, not designing or building one.

grahama1970/anonymize-data

Anonymize supported CSV, JSON, UTF-8 text, and SQLite files using an explicit policy through the oai-trial project. Use for anonymize data, pseudonymize exports, redact policy literals, or discover and explicitly approve fuzzy name aliases. The skill is a thin CLI/Docker interface, not another engine.

grahama1970/anvil

Heavy-duty "No-Vibes" debugging and hardening orchestrator. Use this for complex, stubborn bugs where `review-code` has failed, or for "Red Teaming" (hardening) a codebase. Runs multiple agents in parallel (Thunderdome) using git worktree isolation.

grahama1970/argue

Multi-persona structured debate orchestrator. Personas research via /dogpile, consult colleagues via /ask, and argue toward nuanced synthesis on complex questions.

grahama1970/arxiv

Search arXiv for papers and extract knowledge into memory. Use `search` to find papers, `learn` to extract knowledge.

grahama1970/ask

Use when the user asks to query project memory, ask an oracle, use supported browser-backed reviewers, run Tau roundtable/single-handler workflows, ask Pi-native subagents from within Pi, run persona/deep-review workflows, generate image prompts, check OS/project health through composed skills, or run an ask DAG. This skill is the executable /ask runtime; do not replace it with an informal subagent, plain web search, or hand-written review; inside Pi, explicit Pi-native subagent targets are routed through the pi-subagents tool as an Ask target type.

grahama1970/assess

Step back and critically reassess project state. Use when asked to "assess", "step back", "fresh eyes", "check alignment", "sanity check", "health check", "prune documentation", or "evaluate what's working". Offers documentation pruning and doc-code alignment analysis. Offer to run after major changes (don't auto-run).

grahama1970/assistant

Shared GPT + classifier inference gateway for persona monitor tasks. Routes validation and classification through a 4-tier cascade: heuristic → classifier → local GPT → scillm.

grahama1970/assistant-lab

Self-improvement workbench for /assistant. All the tools needed to diagnose, train, evaluate, and promote models in a continuous loop. The "warm pond" where /assistant evolves its own inference stack.

grahama1970/batch-quality

Pre-flight validation and quality gates for batch LLM operations. ACTUALLY tests samples through LLM before burning tokens. Uses SPARTA contracts for DuckDB validation queries. Integrates with task-monitor for enforced quality gates.

相關技能