Communitygithub.com

grahama1970/apple-design

苹果界面设计方法在Web上的实现:流体交互、弹簧动画、拖拽/滑动/面板手势、半透明材质与深度。适用于构建或评审手势驱动型 UI、弹簧动画、拖拽/滑动/面板交互、动量与可中断过渡、半透明材质与深度、排版(光学尺寸、字间距、行距)、减少运动偏好或苹果风格界面的设计基础(反馈、空间一致性、克制)

apple-design 是什么?

该技能将苹果在《设计流体界面》(WWDC 2018)等演讲中阐述的交互设计原则,翻译为 Web 平台(CSS、Pointer Events、requestAnimationFrame、Motion/Framer Motion 等弹簧库)的具体实践。核心思想是:**当界面从当前屏幕值出发、继承用户速度、向前投影动量,并能在任意时刻被抓取和反转时,它就有了生命。** 弹簧是实现这一切的基石,因为弹簧天然可中断且感知速度。技能覆盖了从响应速度、直接操控、可中断性、弹簧行为与参数、速度交接、动量投影、空间一致性、手势设计细节(点击、拖拽、滑动)、逐帧平滑(transform/opacity 合成、will-change)、材质与深度(backdrop-filter、色斑)到多模态反馈和减少运动等 14 个原则。它提供具体代码示例、默认阻尼/响应参数(如阻尼 1.0、响应 0.4)、弹簧库映射(bounce + duration),并强调所有动画必须是从当前值开始、可被手势实时打断和重定向。该技能适用于 Claude Code、Cursor、Codex 等 AI 编码代理,帮助工程师在 Web 上实现苹果级别的流体交互。

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

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Apple Design

苹果如何构建那些不再像电脑、而更像你自身延伸的界面。这些知识源自苹果 WWDC 的设计演讲——主要是《设计流体界面》(WWDC 2018)——经过提炼并转化为 Web 平台(CSS、Pointer Events、requestAnimationFrame、Motion/Framer Motion 等弹簧库)。

贯穿始终的主线是:当界面的运动从当前屏幕显示的值开始,继承用户的速度,向前投影动量,并且可以在任意时刻被抓住并反转时,它就有了生命。 弹簧是实现这一切自然流畅的工具,因为它天生可中断且感知速度。

核心理念

"当我们将界面与我们思考和移动的方式对齐时,神奇的事情发生了——它不再感觉像一台电脑,而开始感觉像我们自身的无缝延伸。"

当界面像物理世界一样运作时,它就是流畅的:事物瞬间响应,连续移动,携带动量,在边界处产生阻力,并且可以在运动中途被重新导向。以下是实现这一目标的各种方法。

苹果将设计定义为服务于人类的四种需求:安全/可预测性、理解、成就和愉悦。 每一条规则都在服务于其中某一种需求。

1. 响应——消灭延迟

一旦出现滞后,直接操控的感觉就会"断崖式下跌"。响应是所有其他一切的基础。

  • 在指针按下时响应,而不是在释放时。 按钮被按下的瞬间就要高亮。等待 click/触摸抬起才显示反馈会让人感觉死气沉沉。
  • 警惕每一次延迟。 审查去抖、人为计时器、过渡等待以及约 300ms 的点击延迟。输入路径上任何非必要的东西都是退化。
  • 反馈必须在交互过程中持续,而不仅仅在结束时。 对于拖拽、滑块或抽屉,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. 可中断性——最重要的原则

"思维和手势是并行发生的。"

每个动画必须可以在任何时刻被中断和重定向。用户必须能够抓住一个正在移动的元素并反向移动它,而无需等待动画完成。一个正在关闭的、用户又重新抓住的模态框应该跟随手指——而不是先完成关闭再重新打开。

  • 永远不要在过渡期间锁定输入。
  • 始终从展示(当前)值开始动画,而不是目标值。 发生中断时,读取元素的实时屏幕变换,并从那里开始新的动画。从逻辑/目标值开始会导致可见的跳跃。
  • 避免对任何手势驱动的内容使用 CSS 过渡和 @keyframes——它们无法在运动中途被平滑地抓住和反转。弹簧默认从当前值开始动画,这正是中断所需要的。
  • 当手势反转时,混合速度——不要硬切。 在反转点用一个动画替换另一个动画会产生速度不连续性,就像撞到"砖墙"。弹簧库在重新定位时会携带速度,从而避免这一点。(这就是 iOS 的加法动画的原生做法;在 Web 上,选择一种能从当前速度重新定位的弹簧库。)
  • 将二维运动分解为独立的 X 和 Y 弹簧。 当 X 和 Y 速度不同时,一个二维距离上的单一弹簧会失去同步。

4. 行为优于动画——使用弹簧

"将动画视为你与物体之间的对话,而不是界面强加的东西。"

预设好固定时长的动画无法响应新的输入。而弹簧可以——新的输入只需改变目标,运动就能保持连续。对于任何用户可以触摸的东西,都使用弹簧。

苹果有意将物理三要素(质量/刚度/阻尼)替换为两个设计师友好的参数。用这些来思考:

  • 阻尼比——控制过冲。1.0 = 临界阻尼,无弹跳,平滑稳定。< 1.0 = 过冲并振荡。值越低越有弹性。
  • 响应——值到达目标的速度,以秒为单位。值越低越快。这不是"持续时间"——弹簧没有固定时长;它的稳定时间由参数共同决定。

默认值:

  • 大多数 UI 起始设为阻尼 1.0(临界阻尼)——优雅且不分散注意力。
  • 仅当手势自身携带动量时(轻拂、抛掷、拖拽释放)才添加弹跳(阻尼约 0.8)。一个刚刚淡出的菜单过度反弹感觉不对;而你轻拂的卡片过度反弹则感觉自然。

苹果实际使用的具体数值:

交互阻尼响应
移动/重新定位(例如画中画)1.00.4
旋转0.80.4
抽屉/面板0.80.3

Web 映射(Motion / Framer Motion): bounce + duration 弹簧 API 与苹果的阻尼+响应非常接近。一种安全的基线样式是默认在所有地方使用 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. 动量投影——动画到手势要去的地方

"用小输入产生大输出。"

不要从释放点快照到最近的边界。利用速度投影静止位置——就像滚动减速一样——然后快照到投影点最近的`目标。这就是为什么轻拂会感觉像把元素扔出去。

苹果的精确投影函数(来自《设计流体界面》示例代码):

// 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) 不是苹果实际使用的——请使用上述指数衰减形式。这是好的底部面板和轮播(Vaul、Embla)中的标准行为。

7. 空间一致性——对称路径、锚定原点

"如果某样东西以一种方式消失,我们期望它从它来的地方出现。"

  • 进入和退出应走同一路径。 从右侧滑入的面板必须从右侧消失。从右侧进入/从底部出去会让人感觉脱节和困惑。
  • 将交互锚定到其来源。 菜单、弹窗或面板应从触发它的元素处开始——将 transform-origin 设置为触发器,这样按钮和内容之间的空间关系就显而易见了。(这与弹窗从其触发器缩放而不是其中心缩放的原点感知是一样的。)
  • 在可逆的过渡上镜像缓动,以便退出路径与返回路径匹配(为两个方向使用反相三次贝塞尔控制点)。

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. 逐帧平滑度

平滑度关乎帧中有什么,而不仅仅是帧率。

  • 保持每帧位置变化低于感知阈值,以避免频闪效应。
  • 对于非常快的运动,细微的运动模糊/拉伸能编码速度,比锐利的硬块能更好读。
  • requestAnimationFrame 是 Web 的显示同步时钟(苹果使用 CADisplayLink)。仅对合成友好的属性——transform 和 opacity——进行动画,并在运动即将发生时使用 will-change 提示。

12. 材质与深度——半透明传达层级

苹果使用半透明材质作为浮动的功能层,带来结构感而不抢焦点。在 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. 因果性——反馈的原因必须显而易见。在实际的因果事件(开关翻转、项目卡入到位)时触发它,并使它的特征与动作的物理特性相匹配。
  2. 和谐——视觉、声音和触觉必须在同一帧触发。它们之间的延迟会摧毁错觉。不要让 CSS 过渡滞后于音频/触觉(Vibration API)。
  3. 实用性——只在有价值的地方添加反馈。保留触觉/声音给有意义的时刻(成功、错误、确认、对齐)。过度反馈会让用户忽略所有反馈。

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.

相关技能