design-with-taste

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Design with Taste

用格调做设计

This skill encodes the design philosophy behind Family — a product widely praised for feeling alive, welcoming, and intentional. Originally documented by Benji Taylor at benji.org/family-values.
Read this before writing any UI code. Every time.
The user wants something built. Your job is to make it feel like a human who gives a shit designed it.

本技能承载了Family背后的设计理念——这款产品因富有生命力、亲和力且设计用心而广受赞誉。该理念最初由Benji Taylor在benji.org/family-values文档中提出。
在编写任何UI代码前,请先阅读本文。每次都要。
用户需要构建某个功能,你的职责是让它看起来像是由真正用心的人设计的。

The Three Pillars

三大核心支柱

Ordered by priority. You cannot have Delight without Fluidity, and you cannot have Fluidity without Simplicity.

按优先级排序。没有流畅性就谈不上愉悦感,没有简洁性就谈不上流畅性。

1. Simplicity — Gradual Revelation

1. 简洁性——渐进式信息呈现

"Each action by the user makes the interface unfold and evolve, much like walking through a series of interconnected rooms."
The problem: Most UIs dump everything at once — every feature, every option, every edge case, all visible, all the time. This transfers cognitive burden from the designer to the user.
The principle: Show only what matters right now. The interface should feel like walking through rooms — you glimpse what's next before you arrive.
Rules:
  • One primary action per view. Two equally weighted CTAs = failure. Make everything else secondary.
  • Progressive disclosure over feature dumps. Layered trays, step-by-step flows, expandable sections. Never show a 12-field form when 3 steps of 4 fields works.
  • Context-preserving overlays over full-page navigations. Sheets/trays/modals that overlay the current context keep users oriented. Full-screen transitions displace them.
  • Vary heights of stacked layers. Each subsequent sheet/tray must be a visibly different height so the progression is unmistakably clear. Never stack two identical-height layers.
  • Every sheet/tray/modal needs a title and dismiss action. Users must always know what they're looking at and how to get back.
  • Trays adapt to context. A tray appearing within a dark-themed flow should adopt a darker color scheme. The visual environment follows the user.
  • Trays can launch full-screen flows. A compact tray is a valid entry point for a multi-step full-screen experience — don't force a binary choice between "tray" and "page."
  • Use trays for transient actions; full screens for persistent destinations. Confirmations, warnings, and contextual info = tray. Settings, core content = full screen.
jsx
// 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>
Self-check: Can the user tell within 1 second what to do next? If not, simplify.

“用户的每一次操作,都应让界面像穿梭于相互连通的房间一样逐步展开和演进。”
现存问题:大多数UI会一次性展示所有内容——所有功能、所有选项、所有边缘情况,全程可见。这将认知负担从设计师转移到了用户身上。
核心原则:只展示当前最关键的信息。界面应给人一种穿梭房间的感觉——在到达下一个空间前,就能瞥见前方的内容。
规则
  • 每个视图仅保留一个核心操作。 两个权重相同的CTA(号召性按钮)就是设计失败。其他所有操作都应作为次要选项。
  • 用渐进式披露替代信息轰炸。 使用分层面板、分步流程、可展开区域。永远不要展示包含12个字段的表单,改用3个步骤、每步4个字段的形式。
  • 用保留上下文的覆盖层替代整页导航。 覆盖当前上下文的面板/弹窗/模态框能让用户保持方向感,而全屏跳转则会让用户迷失。
  • 堆叠层的高度要有差异。 后续弹出的每个面板高度必须明显不同,确保用户能清晰感知操作流程。绝不能堆叠两个高度相同的层。
  • 每个面板/弹窗/模态框都需要标题和关闭操作。 用户必须随时知道自己当前查看的内容,以及如何返回上一级。
  • 面板需适配上下文。 如果在深色主题流程中弹出面板,面板应采用深色配色方案。视觉环境需跟随用户的操作场景。
  • 面板可触发全屏流程。 紧凑的面板可以作为多步骤全屏体验的入口——不要强迫用户在“面板”和“页面”之间做二元选择。
  • 临时操作用面板,持久目标用全屏。 确认、警告、上下文信息等使用面板;设置、核心内容等使用全屏页面。
jsx
// 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. Fluidity — Seamless Transitions

2. 流畅性——无缝过渡

"We fly instead of teleport."
The problem: Static transitions make products feel dead. A dead product feels uncared for. Instant cuts destroy spatial orientation — where did that come from? Where did it go?
The principle: Treat your app as a space with unbreakable physical rules. Know why a transition makes sense architecturally before adding it. Every element moves from somewhere to somewhere.
Rules:
  • No instant show/hide. Every element that appears or disappears must animate. Pick a transition that makes spatial sense — fade, slide, scale, morph.
  • Shared element transitions. If an element exists in both State A and State B (a card that expands, a button that becomes a sheet), it must visually travel between them. Never unmount and remount — morph.
  • Directional consistency. Navigate right (next step, next tab) → content enters from right. Go back → content enters from left. Tabs to the left of current slide left. This builds spatial memory.
  • Text morphing over instant replacement. When button labels change (e.g., "Continue" → "Confirm"), animate the transition. Identify shared letter sequences ("Con") — keep them fixed while the rest morphs. Use torph (
    npm i torph
    ) — dependency-free, works with React/Vue/Svelte. Crossfade is the minimum fallback; shared-letter morphing is the ideal.
  • Partial text changes: only animate what changes. If a sentence gains or loses a word, keep the unchanged portion static. Animating unchanged text creates jarring redundancy.
  • Persistent elements stay put. If a header, card, or component persists across a transition, it must NOT animate out and back in. Only the changing parts move.
  • Loading states travel to their destination. A spinner doesn't just sit where triggered — it moves to where the user will look for results (e.g., after submitting a transaction, the spinner migrates to the activity tab icon).
  • Micro-directional cues. Chevrons, arrows, and carets should animate to reflect the action taken. A
    becomes a
    on back-navigation. An accordion chevron rotates on expand.
  • Unified interpolation. All visual elements driven by the same data should share the same lerp/easing. This makes the interface feel like one thing breathing rather than a bunch of parts updating independently. When the value changes, the line, the label, the axis, and the badge should all move as one.
jsx
// 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] }}
/>
The golden easing curve:
cubic-bezier(0.16, 1, 0.3, 1)
— fast start, gentle settle. Default for all entrances and morphs. Use
ease-in
(
cubic-bezier(0.4, 0, 1, 1)
) for exits only. Never use linear.
Self-check: Record your screen and play back at 0.5x speed. Can you follow every element's journey? Anything that teleports needs a transition.

“我们选择飞行,而非瞬间传送。”
现存问题:静态过渡会让产品显得毫无生气,而缺乏生命力的产品会给人一种无人在意的感觉。瞬间切换会破坏空间方向感——这个内容从哪来?又到哪去了?
核心原则:将你的应用视为一个拥有不可打破物理规则的空间。在添加过渡效果前,先明确其在架构层面的合理性。每个元素的移动都要有来处和去处。
规则
  • 禁止瞬间显示/隐藏。 所有出现或消失的元素都必须添加动画。选择符合空间逻辑的过渡效果——淡入淡出、滑动、缩放、变形。
  • 共享元素过渡。 如果某个元素同时存在于状态A和状态B(比如展开的卡片、演变为面板的按钮),它必须在视觉上移动到目标位置。绝不能先卸载再重新挂载——要做变形过渡。
  • 方向一致性。 向右导航(下一步、下一个标签页)→ 内容从右侧进入;返回上一级 → 内容从左侧进入。当前标签页左侧的标签页向左滑动切换。这能帮助用户建立空间记忆。
  • 文本变形而非瞬间替换。 当按钮标签变化时(例如“继续”→“确认”),要为切换添加动画。识别共享的字母序列(比如“Con”)——固定这些字母,仅对其余部分做变形。使用torph
    npm i torph
    )——无依赖,支持React/Vue/Svelte。交叉淡入淡出是最低要求;共享字母变形是理想方案。
  • 部分文本变化:仅动画变化的部分。 如果句子中新增或删除了某个单词,保持未变化的部分静止。对未变化文本添加动画会造成突兀的冗余感。
  • 持久元素保持静止。 如果标题、卡片或组件在过渡过程中持续存在,绝不能让它先消失再重新出现。只有变化的部分才需要移动。
  • 加载状态移动到结果展示位置。 加载动画不应停留在触发位置——要移动到用户预期查看结果的区域(例如提交交易后,加载动画迁移到活动标签页图标处)。
  • 微方向提示。 Chevron(V形图标)、箭头和 caret(插入符)应随操作变化而动画。比如返回导航时,
    变为
    ;展开折叠面板时,V形图标旋转。
  • 统一插值。 由同一数据驱动的所有视觉元素应使用相同的插值/缓动函数。这会让界面感觉像是“一个整体在呼吸”,而非多个独立部分的更新。当数据值变化时,线条、标签、坐标轴和徽章应同步移动。
jsx
// 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 — Selective Emphasis

3. 愉悦感——选择性强调

"Mastering delight is mastering selective emphasis."
The problem: Either zero personality (corporate slop) or everything bounces and sparkles (annoying). Both miss the point.
The principle: The Delight-Impact Curve — the less frequently a feature is used, the more delightful it should be. Daily actions need efficiency with subtle touches. Rare moments deserve theatrical ones.
Delight ↑
        |         *  (rare features: theatrical)
        |       *
        |     *
        |   * *  (medium: memorable)
        | * *
        |* *  *  (frequent: subtle)
        +------------------→ Feature frequency
Rules:
  • Polish everything equally. The settings page, the empty state, the error screen — all receive the same care as the hero section. One unpolished corner makes the whole feel unpolished. "Like a fancy restaurant with a dirty bathroom."
  • Easter eggs reward exploration. Hide moments in unexpected places. They create stories users share. Key: place them in features used just enough that discovery feels like reward, not annoyance.
  • Celebrate completions. Significant actions (backup, onboarding, first transaction) deserve confetti, a custom animation, a satisfying sound — not a green checkmark.
  • Make destructive actions satisfying. Deleting items? They tumble into a trash can with a sound effect. Destructive ≠ unpleasant.
  • Animate numbers and live charts. Values that change (prices, counts, balances) should count/flip/morph. Commas should shift position smoothly as numbers grow — never just swap. For real-time line charts, use liveline (
    npm i liveline
    ) — one canvas, no dependencies beyond React 18, 60fps interpolation. For 60fps value overlays, update the DOM directly rather than through React state to avoid re-render overhead.
  • Empty states are first impressions. An animated arrow pointing toward the create button, a floating illustration, a warm message. Never "No items yet" with nothing else.
  • Sound design amplifies physicality. Completion sounds, subtle interaction feedback. Sound reinforces reward and makes actions feel real.
  • Drag-and-drop should feel satisfying. Stacking animations, smooth reorder, visual feedback on lift. Reordering items should feel better than the result deserves.
Delight pattern library — concrete moments proven to work:
FeatureFrequencyDelight LevelPattern
Number inputDailySubtleCommas shift position as digits are typed
Tab/chart navigationDailySubtleArrow icon flips direction with value change
Empty stateFirst visitMediumAnimated arrow + floating illustration
Item reorderOccasionalMediumStacking animation + smooth drop
Delete/trashOccasionalMediumItem tumbles into skeuomorphic trash + sound
First feature useOnceHighAnimated guide arrow in empty state
Critical completion (backup, onboarding)OnceTheatricalConfetti explosion + celebratory sound
Easter egg (QR, hidden gesture)RareTheatricalRipple on tap → sequin effect on swipe
jsx
// 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>
  );
}
Self-check: Show your UI to someone for 30 seconds. Do they smile? If not, add delight. Do they look annoyed? You over-delighted a high-frequency interaction.

“掌握愉悦感的核心是掌握选择性强调。”
现存问题:要么毫无个性(千篇一律的企业风格),要么所有元素都在弹跳闪烁(令人厌烦)。这两种情况都偏离了设计的初衷。
核心原则愉悦感-影响曲线——功能的使用频率越低,其愉悦感设计就应越突出。日常操作需要高效性和细微的交互细节;罕见时刻则值得采用富有戏剧性的设计。
愉悦感 ↑
        |         *  (稀有功能:戏剧性设计)
        |       *
        |     *
        |   * *  (中等频率:令人难忘)
        | * *
        |* *  *  (高频操作:细微交互)
        +------------------→ 功能使用频率
规则
  • 所有部分同等打磨。 设置页面、空状态、错误页面——所有区域都应与核心展示区得到同等的重视。一个未打磨的角落会让整个产品显得粗糙。“就像一家装修精致但卫生间肮脏的餐厅。”
  • 彩蛋奖励探索行为。 在意想不到的地方隐藏交互彩蛋。它们会成为用户乐于分享的话题。关键:将彩蛋放置在使用频率适中的功能中,让用户的发现过程感觉是一种奖励,而非打扰。
  • 庆祝完成时刻。 重要操作(备份、新手引导、首次交易)应伴随彩屑动画、自定义效果、悦耳的音效——而不只是一个绿色对勾。
  • 让破坏性操作也令人满意。 删除项目时?让它们“滚落”到垃圾桶并伴随音效。破坏性操作≠不愉快的体验。
  • 数字和实时图表添加动画。 变化的值(价格、数量、余额)应采用计数/翻转动画。数字增长时,逗号应平滑移动位置——绝不能直接替换。对于实时折线图,使用liveline
    npm i liveline
    )——单画布,除React 18外无其他依赖,支持60fps插值。对于60fps数值覆盖层,直接更新DOM而非通过React状态,以避免重渲染开销。
  • 空状态是第一印象。 用指向创建按钮的动画箭头、浮动插图、温暖的提示语。绝不能只显示“暂无内容”。
  • 音效设计增强真实感。 完成提示音、细微的交互反馈音。音效能强化奖励感,让操作感觉更真实。
  • 拖拽操作应令人满足。 堆叠动画、流畅的重排序、抬起时的视觉反馈。重新排序的体验应比最终结果更令人愉悦。
愉悦感设计模式库——经验证有效的具体场景:
功能使用频率愉悦感等级设计模式
数字输入每日细微输入数字时逗号平滑移动位置
标签页/图表导航每日细微箭头图标随数值变化翻转方向
空状态首次访问中等动画箭头+浮动插图
项目重排序偶尔中等堆叠动画+平滑放置
删除/垃圾桶偶尔中等项目“滚落”到拟物化垃圾桶+音效
首次使用功能一次空状态中的动画引导箭头
关键操作完成(备份、新手引导)一次戏剧性彩屑爆炸+庆祝音效
彩蛋(二维码、隐藏手势)稀有戏剧性点击时产生涟漪→滑动时产生亮片效果
jsx
// 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>
  );
}
自我检查:让别人看30秒你的UI。他们会微笑吗?如果不会,就添加愉悦感设计。他们会感到厌烦吗?说明你在高频交互中过度添加了愉悦感效果。

The Taste Checklist

格调检查清单

Run before considering any UI "done":
在认为UI“完成”前,先完成以下检查:

Simplicity

简洁性

  • Each screen has ONE clear primary action
  • Complex flows broken into digestible steps
  • Information revealed progressively, not all at once
  • Context preserved during transitions (overlays > navigations)
  • Stacked layers are visibly different heights
  • Every overlay has a title and dismiss action
  • User always knows where they are and how to go back
  • 每个页面都有一个清晰的核心操作
  • 复杂流程拆分为易于理解的步骤
  • 信息逐步呈现,而非一次性展示
  • 过渡过程中保留上下文(覆盖层优先于导航)
  • 堆叠层的高度明显不同
  • 每个覆盖层都有标题和关闭操作
  • 用户始终知道自己所处位置及返回方式

Fluidity

流畅性

  • Zero instant show/hide — everything animates
  • Shared elements morph between states (not unmount/remount)
  • Directional transitions match spatial logic
  • Persistent elements don't redundantly animate
  • Text changes use torph or crossfade minimum
  • Only the changing part of partial text updates animates
  • Loading states move to where results will appear
  • Micro-directional cues on chevrons and arrows
  • Elements driven by same data share the same lerp/easing (unified interpolation)
  • Default easing is
    cubic-bezier(0.16, 1, 0.3, 1)
  • 无瞬间显示/隐藏——所有元素都有动画
  • 共享元素在状态间变形(而非卸载/重新挂载)
  • 方向过渡符合空间逻辑
  • 持久元素无冗余动画
  • 文本变化使用torph或至少交叉淡入淡出
  • 仅文本中变化的部分添加动画
  • 加载状态移动到结果展示位置
  • V形图标和箭头有微方向提示
  • 同一数据驱动的元素使用相同的插值/缓动(统一插值)
  • 默认缓动曲线为
    cubic-bezier(0.16, 1, 0.3, 1)

Delight

愉悦感

  • Frequent features have subtle micro-interactions
  • Infrequent features have memorable moments
  • Empty states are designed, not afterthoughts
  • Completions are celebrated (not just a checkmark)
  • Numbers animate when they change
  • At least one easter egg or hidden moment
  • All corners equally polished — no dirty bathrooms
  • At least one moment makes someone say "oh, that's nice"
  • 高频功能有细微的微交互
  • 低频功能有令人难忘的设计
  • 空状态经过精心设计,而非事后补全
  • 完成操作有庆祝效果(不只是对勾)
  • 数字变化时有动画
  • 至少包含一个彩蛋或隐藏交互
  • 所有区域同等打磨——无“肮脏的卫生间”
  • 至少有一个时刻能让用户说“哦,这不错”

General Taste

整体格调

  • No generic AI aesthetics (Inter font, purple gradients, cookie-cutter layouts)
  • Typography is intentional — display + body pairing
  • Color palette has a dominant color with sharp accents
  • Spacing is generous and consistent
  • Interface feels like a physical space, not a slideshow
  • Every pixel looks placed by someone who cares

  • 无通用AI美学(Inter字体、紫色渐变、千篇一律的布局)
  • 排版有明确意图——标题与正文字体搭配合理
  • 配色方案有主色调和鲜明的强调色
  • 间距宽松且一致
  • 界面给人一种物理空间的感觉,而非幻灯片
  • 每一个像素都看起来是由用心的人放置的

Anti-Patterns — Things That Kill Taste

反模式——破坏格调的行为

  1. Static tab switches. No directional slide = digital whiplash.
  2. Modals that pop from nowhere. Grow from trigger or slide from edge. Never just
    opacity: 0 → 1
    centered.
  3. Skeleton screens that don't match the real layout. If the skeleton has 3 bars and the content has 5 lines, you've broken the illusion.
  4. Redundant animations. A persistent header that fades out and back in during a page transition. Persistent = stays.
  5. Linear easing. Nothing in the physical world moves linearly.
  6. "No items" empty text. First impression. Treat it like one.
  7. Uniform sizing in stacked layers. Two sheets the same height = no sense of depth or progression.
  8. Toasts for important outcomes. Toasts = background info. Success/error/completion = inline, contextual, animated.
  9. Forms that are just stacked inputs. Step-by-step with transitions between them.
  10. Buttons that don't respond to interaction. Hover, active, and focus states. Always.
  11. Animating unchanged text. If only one word changes in a sentence, only that word moves.
  12. Spinner left at origin after action. Move it to where the user will look for the result.

  1. 静态标签页切换。 无方向滑动=数字版“鞭打”体验。
  2. 凭空出现的模态框。 从触发元素展开或从边缘滑入。绝不能只是
    opacity: 0 → 1
    的居中显示。
  3. 与真实布局不符的骨架屏。 如果骨架屏显示3条占位符,但实际内容有5行,就打破了用户的预期。
  4. 冗余动画。 页面过渡时,持续存在的标题先消失再重新出现。持久元素=保持静止。
  5. 线性缓动。 现实世界中没有任何物体是匀速运动的。
  6. 仅显示“暂无内容”的空状态。 这是用户的第一印象,要像对待核心区域一样设计它。
  7. 堆叠层尺寸统一。 两个高度相同的面板=无深度感或流程感。
  8. 用提示框展示重要结果。 提示框=后台信息。成功/错误/完成提示应内嵌在上下文环境中并添加动画。
  9. 仅堆叠输入框的表单。 拆分为带过渡效果的分步流程。
  10. 无交互反馈的按钮。 必须有悬停、激活、聚焦状态。
  11. 未变化文本添加动画。 如果句子中只有一个单词变化,仅对该单词添加动画。
  12. 操作后停留在原地的加载动画。 将其移动到用户预期查看结果的位置。

Easing & Timing Reference

缓动与时间参考

Use CaseEasingDuration
Element entering
cubic-bezier(0.16, 1, 0.3, 1)
300–400ms
Element exiting
cubic-bezier(0.4, 0, 1, 1)
200–250ms
Shared element morph
cubic-bezier(0.16, 1, 0.3, 1)
350–500ms
Micro-interaction (hover, press)
cubic-bezier(0.2, 0, 0, 1)
100–150ms
Spring (bouncy)
damping: 20, stiffness: 300
auto
Spring (smooth)
damping: 30, stiffness: 200
auto
Number countingease-out cubic400–800ms
Page transition
cubic-bezier(0.16, 1, 0.3, 1)
300ms
Stagger between items30–60ms per item

使用场景缓动曲线时长
元素入场
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
元素间的 stagger 延迟30–60ms 每个元素

Recommended Tools

推荐工具

These libraries are built by the same people behind Family and embody the same philosophy:
LibraryPurposeInstall
torphDependency-free text morphing. Handles shared-letter transitions automatically. React, Vue, Svelte.
npm i torph
livelineReal-time animated line charts. One canvas, 60fps lerp, momentum arrows, no dependencies beyond React 18.
npm i liveline
When building anything with text that changes or live numeric/chart data, reach for these before rolling your own.

这些库由Family背后的团队开发,与Family的设计理念一致:
用途安装命令
torph无依赖的文本变形库。自动处理共享字母过渡。支持React、Vue、Svelte。
npm i torph
liveline实时动画折线图。单画布,60fps插值,动量箭头,除React 18外无其他依赖。
npm i liveline
当需要处理变化的文本或实时数值/图表数据时,优先使用这些库,而非自行开发。

How to Use This Skill

如何使用本技能

  1. Read this before every UI task. Not after. Before.
  2. Apply pillars in order. Simplicity → Fluidity → Delight. You can't polish a bad layout with animations.
  3. Run the checklist before delivering.
  4. Pair with
    frontend-design
    skill
    for visual aesthetics (typography, color, layout). This skill handles feel and interaction quality.
  5. When in doubt, animate. Easier to tone down than to add life to a dead interface.
  6. Record and review at 0.5x. Slow motion reveals every teleport, every jarring cut, every missed opportunity.
The goal is not to make something that "works." The goal is to make something that someone uses and thinks: "Whoever made this actually gives a shit."
That's taste.
  1. 每次UI任务前先阅读本文。 不是之后,是之前。
  2. 按顺序应用三大支柱。 简洁性→流畅性→愉悦感。你无法用动画拯救糟糕的布局。
  3. 交付前完成检查清单。
  4. frontend-design
    技能配合使用
    ,以优化视觉美学(排版、颜色、布局)。本技能专注于交互的“质感”和“体验质量”。
  5. 存疑时,先添加动画。 调弱动画比给死气沉沉的界面注入生命力更容易。
  6. 录制并以0.5倍速回放。 慢动作会暴露所有“瞬间传送”、突兀切换和遗漏的优化点。
我们的目标不是做出“能用”的东西。而是做出用户使用时会想:*“做这个东西的人真的用心了。”*的产品。
这就是格调。