关于 design-with-taste
Design with Taste 是一套指导 UI 设计与实现的理念和检查清单,面向构建界面的开发者。它以简化、流畅过渡和有分寸的愉悦感为原则,提供具体交互模式及反模式。
主要功能
- 01按渐进披露组织界面
- 02设计有空间方向的过渡
- 03为交互设置分层动效
- 04提供设计检查清单
- 05列出常见界面反模式
适用场景
- 01规划分步骤表单和覆盖层
- 02为页面与标签切换设计动效
- 03检查空状态和完成反馈
- 04评审界面的一致性与可用性
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ cristicretu/ family-taste-skill --skill design-with-taste安装后在 Claude Code 中输入/design-with-taste确认已加载。
详细介绍
有品位的设计
这项技能凝练了 Family 背后的设计理念——这款产品广受赞誉,让人感觉鲜活、亲切且用心。该理念最初由 Benji Taylor 记录于 benji.org/family-values。
每次编写 UI 代码前,都要阅读本文。
用户想要构建某样东西。你的工作是让它看起来像是出自一个真正在乎设计的人之手。
三大支柱
按优先级排序。没有流畅性,就谈不上愉悦;没有简洁性,也就无从实现流畅性。
1. 简洁性——循序渐进地呈现
“用户的每个操作都会让界面逐步展开和演变,就像穿过一系列相互连通的房间。”
问题:大多数 UI 一次性倾倒所有内容——每项功能、每个选项、每个边缘情况,始终全部可见。这把认知负担从设计师转移到了用户身上。
原则:只展示当下重要的内容。界面应像穿过一间间房间一样,让用户在抵达之前就能瞥见接下来的内容。
规则:
- 每个视图只设一个主要操作。 两个权重相同的 CTA 就是失败。其他操作都应设为次要操作。
- 采用渐进式披露,而不是一次性罗列功能。 使用分层托盘、分步流程、可展开区块。不要在 3 步、每步 4 个字段即可完成时,展示包含 12 个字段的表单。
- 优先使用保留上下文的叠加层,而不是整页跳转。 覆盖当前上下文的抽屉/托盘/模态框能帮助用户保持方向感。全屏切换则会让用户脱离当前情境。
- 叠放的层级要有不同高度。 后续每个抽屉/托盘都必须明显不同高,让层级变化一目了然。不要叠放两个高度相同的层级。
- 每个抽屉/托盘/模态框都需要标题和关闭操作。 用户必须始终知道自己在看什么,以及如何返回。
- 托盘应适应上下文。 在深色主题流程中出现的托盘应采用较深的配色。视觉环境应跟随用户。
- 托盘可以启动全屏流程。 精简托盘可以作为多步全屏体验的有效入口——不要强迫用户在“托盘”和“页面”之间二选一。
- 临时操作使用托盘;持久目的地使用全屏页面。 确认、警告和上下文信息用托盘;设置和核心内容用全屏页面。
// GOOD: Progressive tray — compact, focused, context-aware
<Sheet>
<SheetTrigger>Confirm Send</SheetTrigger>
<SheetContent className="h-[45vh]"> {/* height varies from parent */}
<SheetHeader>
<SheetTitle>Review Transaction</SheetTitle>
<DismissButton />
</SheetHeader>
{/* Core info only — no extras */}
<Button>Send $42.00</Button>
</SheetContent>
</Sheet>
自检:用户能否在 1 秒内判断接下来该做什么?如果不能,就继续简化。
2. 流畅性 — 无缝过渡
“我们飞行,而不是瞬移。”
问题:静态过渡会让产品显得毫无生气。没有生气的产品会让人觉得无人用心。瞬间切换会破坏空间方位感——它从哪里来?又去了哪里?
原则:把你的应用视为一个遵循不可违背的物理规则的空间。添加过渡前,先弄清楚它在架构上为何合理。每个元素都从某处移动到某处。
规则:
- 不要瞬间显示或隐藏。 每个出现或消失的元素都必须有动画。选择符合空间逻辑的过渡方式——淡入淡出、滑动、缩放或变形。
- 共享元素过渡。 如果某个元素在状态 A 和状态 B 中都存在(例如展开的卡片,或变成面板的按钮),它必须在视觉上从一处移动到另一处。绝不要卸载后再重新挂载——应让它变形。
- 保持方向一致。 向右导航(下一步、下一个标签页)→ 内容从右侧进入。返回 → 内容从左侧进入。切换到当前标签页左侧的标签页时,内容向左滑动。这会建立空间记忆。
- 文字变形优于瞬间替换。 按钮标签发生变化时(例如从 “Continue” 变为 “Confirm”),要为过渡添加动画。找出相同的字母序列(“Con”)——让它们保持不动,同时让其余部分变形。使用 torph(
npm i torph)——无依赖,适用于 React/Vue/Svelte。交叉淡入淡出是最低限度的后备方案;字母共享变形是理想方案。 - 文字仅部分变化时,只为变化部分添加动画。 如果句子增加或删去了一个词,未变化的部分应保持静止。为未变化的文字添加动画会造成突兀的重复感。
- 持久元素保持原位。 如果标题栏、卡片或组件在过渡前后都存在,就绝不能让它先动画退出再重新进入。只有变化的部分才移动。
- 加载状态应移动到目标位置。 加载指示器不应只停留在触发它的位置——它应移动到用户期待查看结果的位置(例如,提交交易后,加载指示器移动到活动标签页图标上)。
- 微观方向提示。 尖括号、箭头和插入符号应通过动画反映所执行的操作。返回导航时,
→变为←。手风琴的尖括号在展开时旋转。 - 统一插值。 由同一数据驱动的所有视觉元素应使用相同的 lerp/缓动函数。这样,界面会像一个整体在呼吸,而不是一堆部分各自独立更新。值发生变化时,线条、标签、坐标轴和徽章都应同步移动。
// Text morphing — use torph
import { TextMorph } from 'torph/react';
<TextMorph>{label}</TextMorph> // handles shared-letter animation automatically
// Directional tab transitions
const direction = newIndex > currentIndex ? 1 : -1;
<motion.div
key={currentTab}
initial={{ x: direction * 20, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
exit={{ x: -direction * 20, opacity: 0 }}
transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1] }}
/>
// Shared element: card → detail view
<motion.div
layoutId={`card-${id}`}
className={isExpanded ? "fixed inset-0 rounded-none" : "rounded-xl"}
transition={{ duration: 0.4, ease: [0.16, 1, 0.3, 1] }}
/>
黄金缓动曲线:cubic-bezier(0.16, 1, 0.3, 1)——快速开始,平缓收尾。所有进入和变形动画的默认曲线。退出时才使用 ease-in(cubic-bezier(0.4, 0, 1, 1))。绝不要使用线性曲线。
自检:录制屏幕,并以 0.5 倍速回放。你能跟上每个元素的移动轨迹吗?任何瞬间跳转的元素都需要添加过渡。
3. 愉悦感 — 选择性强调
“掌握愉悦感,就是掌握选择性强调。”
问题:要么毫无个性(企业风格的空话),要么所有东西都蹦蹦跳跳、闪闪发光(令人厌烦)。两者都不得要领。
原则:愉悦感与影响力曲线——功能使用频率越低,就越应该令人愉悦。日常操作需要高效,并搭配微妙的点缀。罕见时刻则值得戏剧化的表现。
Delight ↑
| * (rare features: theatrical)
| *
| *
| * * (medium: memorable)
| * *
|* * * (frequent: subtle)
+------------------→ Feature frequency
规则:
- 每个地方都要同样精致。 设置页面、空状态、错误页面,都要和主视觉区域一样用心。任何一处粗糙都会让整体显得粗糙。“就像一家华丽餐厅,却有一间脏兮兮的洗手间。”
- 彩蛋能奖励探索。 在意想不到的地方藏一些惊喜,让用户有故事可分享。关键是把它们放在使用频率恰到好处的功能中,让发现彩蛋像是获得奖励,而不是受到打扰。
- 为完成操作庆祝。 重要操作(备份、引导流程、首次交易)值得配上彩纸、定制动画、令人满足的音效,而不只是一个绿色勾号。
- 让破坏性操作也令人满足。 删除条目时,让它们伴随音效滚落进垃圾桶。破坏性操作 ≠ 令人不快。
- 为数字和动态图表添加动画。 发生变化的数值(价格、计数、余额)应该通过计数、翻转或变形来呈现。数字变长时,逗号应平滑移动位置,而不应直接替换。实时折线图可使用 liveline(
npm i liveline)——只需一个 canvas,除 React 18 外没有其他依赖,并可实现 60fps 插值。要以 60fps 更新数值叠加层,应直接更新 DOM,而不是通过 React 状态,以避免重新渲染开销。 - 空状态是第一印象。 可以用动画箭头指向创建按钮,搭配浮动插图和温暖的提示语。绝不要只写“暂无条目”,除此之外什么也没有。
- 音效设计能增强真实感。 完成操作时的音效、微妙的交互反馈,都能强化奖励感,让操作显得真实。
- 拖放操作应该令人满足。 堆叠动画、平滑重排、抓起时的视觉反馈。调整条目顺序的体验应该比结果本身更令人愉悦。
愉悦感模式库——经验证有效的具体设计时刻:
| 功能 | 频率 | 愉悦感等级 | 模式 |
|---|---|---|---|
| 数字输入 | 每天 | 微妙 | 输入数字时,逗号平滑移动位置 |
| 标签页/图表导航 | 每天 | 微妙 | 数值变化时,箭头图标翻转方向 |
| 空状态 | 首次访问 | 中等 | 动画箭头 + 浮动插图 |
| 条目重排 | 偶尔 | 中等 | 堆叠动画 + 平滑放置 |
| 删除/垃圾桶 | 偶尔 | 中等 | 条目滚入拟物化垃圾桶 + 音效 |
| 首次使用功能 | 一次 | 高 | 空状态中显示动画引导箭头 |
| 重要操作完成(备份、引导流程) | 一次 | 戏剧化 | 彩纸爆开 + 庆祝音效 |
| 彩蛋(QR、隐藏手势) | 罕见 | 戏剧化 | 点击时出现涟漪 → 滑动时出现亮片效果 |
// Animated number with smooth comma shifting
function AnimatedNumber({ value }) {
const spring = useSpring(value, { stiffness: 80, damping: 20 });
return <motion.span>{useTransform(spring, v => Math.round(v).toLocaleString())}</motion.span>;
}
// Real-time chart — liveline handles interpolation, momentum arrows, scrub, theming
import { Liveline } from 'liveline';
<div style={{ height: 200 }}>
<Liveline
data={history} // [{ time, value }]
value={latestValue} // current number
momentum // directional arrows (green/red/grey)
showValue // 60fps DOM overlay, no re-renders
color="#3b82f6" // derives full palette from one color
/>
</div>
// Satisfying empty state
function EmptyState() {
return (
<div className="flex flex-col items-center gap-4 py-16">
<motion.div animate={{ y: [0, -8, 0] }} transition={{ repeat: Infinity, duration: 2, ease: "easeInOut" }}>
<IllustrationIcon />
</motion.div>
<p className="text-muted">Nothing here yet</p>
<motion.div animate={{ x: [0, 5, 0] }} transition={{ repeat: Infinity, duration: 1.5 }}>
<ArrowRight className="inline mr-1" /> Create your first item
</motion.div>
</div>
);
}
// Confetti on significant completion
function CompletionScreen() {
useEffect(() => { playSound('success'); }, []);
return (
<motion.div initial={{ scale: 0.8, opacity: 0 }} animate={{ scale: 1, opacity: 1 }}
transition={{ type: "spring", damping: 15, stiffness: 200 }}>
<ConfettiExplosion />
<h2>You're all set!</h2>
</motion.div>
);
}
自检:把你的 UI 展示给某人看 30 秒。他们会微笑吗?如果不会,就增加一些愉悦感。他们看起来很烦躁吗?你给高频交互添加的愉悦感过头了。
品味检查清单
在认定任何 UI“完成”之前,请逐项检查:
简洁
- 每个屏幕只有一个明确的主要操作
- 将复杂流程拆分成易于理解的步骤
- 逐步展示信息,而不是一次性全部呈现
- 转场时保留上下文(优先使用覆盖层,而非页面跳转)
- 堆叠的层级有明显的高度差异
- 每个覆盖层都有标题和关闭操作
- 用户始终知道自己所在的位置,以及如何返回
流畅感
- 不要让内容瞬间显示或隐藏——所有变化都应有动画
- 共享元素应在不同状态间变形(而非卸载后重新挂载)
- 方向性过渡应符合空间逻辑
- 持续存在的元素不应重复播放动画
- 文本变化至少使用 torph 或交叉淡化效果
- 部分文本更新时,只为发生变化的部分添加动画
- 加载状态应移动到结果将出现的位置
- 为折叠符号和箭头添加微妙的方向提示
- 由同一数据驱动的元素应共享相同的 lerp/缓动效果(统一插值)
- 默认缓动为
cubic-bezier(0.16, 1, 0.3, 1)
愉悦感
- 高频使用的功能应有细腻的微交互
- 低频使用的功能应有令人难忘的体验
- 空状态应经过精心设计,而非事后补上
- 完成操作时应予以庆祝(而不只是显示一个勾号)
- 数字变化时应播放动画
- 至少加入一个彩蛋或隐藏体验
- 每个角落都应同样精致——不要留下被忽视的地方
- 至少有一个瞬间能让人说出“哦,这真不错”
整体品味
- 避免千篇一律的 AI 美学(Inter 字体、紫色渐变、模板化布局)
- 字体排印应经过精心设计——搭配展示字体与正文字体
- 配色应有一种主导色,并辅以鲜明的点缀色
- 留白充足且一致
- 界面应像一个真实空间,而非幻灯片
- 每个像素都应看起来经过用心安排
反模式——扼杀品味的设计
- 静态切换标签页。 没有方向性滑动 = 数字世界里的晕头转向。
- 凭空弹出的模态框。 从触发点展开,或从边缘滑入。绝不要只在中央执行
opacity: 0 → 1。 - 与真实布局不符的骨架屏。 如果骨架屏有 3 条横线,而内容有 5 行,你就打破了幻觉。
- 冗余动画。 页面过渡时,持续显示的标题栏先淡出再淡入。持续显示 = 保持不动。
- 线性缓动。 现实世界里没有什么是线性运动的。
- “没有项目”的空状态文案。 这是用户的第一印象。应认真对待。
- 堆叠层大小一致。 两张同高的卡片 = 没有深度感或进展感。
- 用 Toast 提示重要结果。 Toast = 背景信息。成功、错误、完成状态 = 行内显示、结合上下文并带有动画。
- 表单只是输入框的堆叠。 应分步填写,并在步骤间添加过渡效果。
- 按钮对交互毫无响应。 始终提供悬停、按下和聚焦状态。
- 为未变化的文本添加动画。 如果句子中只有一个词变化,就只移动那个词。
- 操作后微调器仍停留在原处。 应将其移动到用户查找结果的位置。
缓动与时长参考
| 使用场景 | 缓动 | 时长 |
|---|---|---|
| 元素进入 | cubic-bezier(0.16, 1, 0.3, 1) | 300–400ms |
| 元素退出 | cubic-bezier(0.4, 0, 1, 1) | 200–250ms |
| 共享元素变形 | cubic-bezier(0.16, 1, 0.3, 1) | 350–500ms |
| 微交互(悬停、按下) | cubic-bezier(0.2, 0, 0, 1) | 100–150ms |
| 弹簧(弹跳) | damping: 20, stiffness: 300 | 自动 |
| 弹簧(平滑) | damping: 30, stiffness: 200 | 自动 |
| 数字递增 | ease-out cubic | 400–800ms |
| 页面过渡 | cubic-bezier(0.16, 1, 0.3, 1) | 300ms |
| 项目间的交错间隔 | — | 每项 30–60ms |
推荐工具
这些库由打造 Family 的同一团队开发,秉承相同的理念:
| 库 | 用途 | 安装 |
|---|---|---|
| torph | 无依赖的文本变形。自动处理共享字母的过渡效果。支持 React、Vue、Svelte。 | npm i torph |
| liveline | 实时动态折线图。使用单个 canvas,以 60fps 进行 lerp,提供动量箭头;除 React 18 外无其他依赖。 | npm i liveline |
构建涉及文本变化或实时数值/图表数据的内容时,优先使用这些工具,而不是自行实现。
如何使用此技能
- 每次执行 UI 任务前都要阅读。 不是之后,而是之前。
- 按顺序应用各项原则。 简洁 → 流畅 → 愉悦。你无法用动画润色糟糕的布局。
- 交付前运行检查清单。
- 搭配
frontend-design技能使用,打造视觉美感(排版、色彩、布局)。此技能负责感受和交互质量。 - 拿不准时,就加上动画。 给动画降调比给死气沉沉的界面注入活力容易。
- 以 0.5 倍速录制并回看。 慢动作会暴露每一次瞬移、每一个突兀的切换、每一个错失的机会。
目标不是做出“能用”的东西,而是做出让人用过后觉得:“做这东西的人是真的在乎。”
这就是品味。