mcpField
D
design-with-taste

design-with-taste

github.com/cristicretu设计与 UI11759 次安装36 周前更新
代码仓库

关于 design-with-taste

Design with Taste 是一套指导 UI 设计与实现的理念和检查清单,面向构建界面的开发者。它以简化、流畅过渡和有分寸的愉悦感为原则,提供具体交互模式及反模式。

主要功能

  1. 01按渐进披露组织界面
  2. 02设计有空间方向的过渡
  3. 03为交互设置分层动效
  4. 04提供设计检查清单
  5. 05列出常见界面反模式

适用场景

  1. 01规划分步骤表单和覆盖层
  2. 02为页面与标签切换设计动效
  3. 03检查空状态和完成反馈
  4. 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 字体、紫色渐变、模板化布局)
  • 字体排印应经过精心设计——搭配展示字体与正文字体
  • 配色应有一种主导色,并辅以鲜明的点缀色
  • 留白充足且一致
  • 界面应像一个真实空间,而非幻灯片
  • 每个像素都应看起来经过用心安排

反模式——扼杀品味的设计

  1. 静态切换标签页。 没有方向性滑动 = 数字世界里的晕头转向。
  2. 凭空弹出的模态框。 从触发点展开,或从边缘滑入。绝不要只在中央执行 opacity: 0 → 1。
  3. 与真实布局不符的骨架屏。 如果骨架屏有 3 条横线,而内容有 5 行,你就打破了幻觉。
  4. 冗余动画。 页面过渡时,持续显示的标题栏先淡出再淡入。持续显示 = 保持不动。
  5. 线性缓动。 现实世界里没有什么是线性运动的。
  6. “没有项目”的空状态文案。 这是用户的第一印象。应认真对待。
  7. 堆叠层大小一致。 两张同高的卡片 = 没有深度感或进展感。
  8. 用 Toast 提示重要结果。 Toast = 背景信息。成功、错误、完成状态 = 行内显示、结合上下文并带有动画。
  9. 表单只是输入框的堆叠。 应分步填写,并在步骤间添加过渡效果。
  10. 按钮对交互毫无响应。 始终提供悬停、按下和聚焦状态。
  11. 为未变化的文本添加动画。 如果句子中只有一个词变化,就只移动那个词。
  12. 操作后微调器仍停留在原处。 应将其移动到用户查找结果的位置。

缓动与时长参考

使用场景缓动时长
元素进入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 cubic400–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

构建涉及文本变化或实时数值/图表数据的内容时,优先使用这些工具,而不是自行实现。


如何使用此技能

  1. 每次执行 UI 任务前都要阅读。 不是之后,而是之前。
  2. 按顺序应用各项原则。 简洁 → 流畅 → 愉悦。你无法用动画润色糟糕的布局。
  3. 交付前运行检查清单。
  4. 搭配 frontend-design 技能使用,打造视觉美感(排版、色彩、布局)。此技能负责感受和交互质量。
  5. 拿不准时,就加上动画。 给动画降调比给死气沉沉的界面注入活力容易。
  6. 以 0.5 倍速录制并回看。 慢动作会暴露每一次瞬移、每一个突兀的切换、每一个错失的机会。

目标不是做出“能用”的东西,而是做出让人用过后觉得:“做这东西的人是真的在乎。”

这就是品味。

评论

支持 Markdown