Details That Make Interfaces Feel Better

Layout

1. 分组

目标: 用户一眼能看出哪些内容属于一起。

  • 同一组内容之间的距离要小,不同组之间的距离要明显更大。
  • 不同组之间的间距,建议至少是组内间距的 2 倍。
  • 优先用留白区分内容;留白不够清楚时再使用背景;最后才使用分割线。
  • 如果仅靠间距已经能看出分组,就不要再加分割线。

2. 对齐

目标: 页面看起来有稳定的结构,不杂乱。

  • 同一级内容尽量沿同一条左边线或右边线排列。
  • 相同类型的内容使用相同的对齐方式。
  • 子内容需要缩进时,同一级子内容使用相同的缩进距离。
  • 不要为了“看起来差不多”随意把某个元素单独向左或向右移动几像素。

3. 内容顺序

目标: 重要内容先被看到,次要内容后被看到。

  • 页面默认按照从上到下、从左到右的顺序安排信息。
  • 最重要的信息放在更靠前的位置。
  • 一行信息中,名称、标题等主要信息放前面;时间、状态、辅助操作等次要信息放后面。
  • 不要让次要信息比主要信息更抢眼。

4. 可点击内容和普通内容要能区分

目标: 用户能看出哪些东西可以点击。

  • 按钮、链接、可操作区域不能看起来和普通文字完全一样。
  • 可以通过背景、边框、下划线、固定按钮区域等方式表示“这里可以操作”。
  • 同一种操作在不同页面中应保持相同的表现方式。

5. 被隐藏的内容必须有提示

目标: 用户知道还有更多内容可以展开、滑动或查看。

  • 如果内容被折叠,必须显示“展开”、箭头等提示。
  • 如果内容可以横向滑动,可以露出下一项的一小部分,让用户知道右边还有内容。
  • 不要把内容完全藏起来,又不给任何提示。

6. 操作按钮之间要留足距离

目标: 多个按钮不会挤在一起,也不容易点错。

  • 有边框或背景的相邻按钮,间距可以从约 12px 开始。
  • 只有文字或图标的操作,周围需要更大的留白,可以从约 24px 开始。
  • 两组不同操作之间的距离,要明显大于同一组按钮之间的距离。

7. 页面边缘的使用

目标: 背景可以铺满屏幕,但文字和操作不要贴边。

  • 背景、图片、视频可以延伸到屏幕边缘。
  • 正文、按钮等主要内容要和屏幕边缘保持稳定距离。
  • 移动端没有现成规范时,左右可以先使用约 16px 的边距。
  • 除非有明确设计目的,否则按钮和文字不要直接贴到屏幕边缘。

8. 视觉对齐优先于数学对齐

目标: 元素看起来是对齐的,而不是只看坐标是否相等。

  • 播放按钮、箭头、尖角图标等形状不对称,即使数学上居中,看起来也可能偏。
  • 这种情况下允许轻微移动 1–2px,让它视觉上更居中。
  • 如果图标本身画歪了,优先修改图标本身,不要长期靠外边距补偿。

Responsive

1. 布局何时切换

目标: 只有当前布局真的放不下时,才切换结构。

  • 不要因为屏幕到了 768px、1024px 就固定切换布局。
  • 当内容开始拥挤、重叠或被压缩到不可用时,再设置断点。
  • 只要原布局仍能正常使用,就尽量保持原结构。
  • 例如:侧边栏开始把正文压得过窄,或卡片已经无法保持合理宽度时,再切换布局。

2. 页面和组件分别适配

目标: 元素根据自己真正拥有的空间变化,而不是只看整个屏幕有多宽。

  • 页面整体布局可以根据浏览器宽度变化。
  • 独立组件应优先根据组件所在容器的宽度变化。
  • 例如一个 Card 即使处于宽屏,如果被放进很窄的侧边栏,也应该切换成窄版布局。
  • 这种组件级适配优先使用 Container Query,而不是只使用全局屏幕断点。

3. 内容变长时布局不能坏

目标: 文本长度变化后,组件仍然能够正常显示。

  • 不要按照当前文案长度给文字容器设置固定宽度。
  • 文字容器不要设置固定高度,需要最低高度时使用 min-height。
  • 文本应该允许换行。
  • Button 宽度应由 文字 + 左右 Padding 决定,不要为文案写死固定宽度。
  • 不同语言可能让文字明显变长,因此布局不能依赖某一种语言的长度。

4. 重要操作不能被裁掉

目标: 屏幕变小、内容变长或键盘弹出时,主要操作仍然可见。

  • Primary Action 不应放在容易被裁掉的位置。
  • 不要把主要按钮固定在一个可能溢出的固定高度区域底部。
  • 如果内容区域需要滚动,主要操作可以放在独立且稳定的底部操作栏中。
  • 固定在屏幕边缘的操作需要避开手机刘海、圆角和底部手势区域。

Typography

1. 字体数量与搭配

目标: 字体体系简单、明确。

  • 整个界面通常不超过 3 种字体。
  • 字体搭配应有明显差异,例如 Serif Heading + Sans Body。
  • 不要同时使用两种看起来非常接近的字体。

2. 字号层级

目标: 通过稳定的字号层级表达信息重要性。

  • 只使用少量固定字号,不为单个页面随意创建新字号。
  • Heading 层级越低,视觉尺寸不得反而越大。
  • 相邻层级可以字号相同,但必须通过字重或间距区分。

3. 字号与字重

目标: 保证文字清晰,同时维持合理层级。

  • 长正文默认从约 16px 开始。
  • Input、Menu 等 UI 文字可从约 14px 开始。
  • Caption 可使用约 13px。
  • 除特殊情况外,不使用低于 12px 的文字。
  • 18px 以下文字使用 400+ 字重。
  • 300 以下的轻字重只用于约 28px+ 的大字号展示文字。

4. 行高与字距

目标: 不同类型文字保持合适的阅读密度。

  • Heading 行高约 1.1。
  • 正文行高约 1.5–1.6。
  • 三行及以上文字,行高至少约 1.4。
  • 大标题可以使用轻微负字距。
  • 小号全大写文字可以使用轻微正字距。
  • 普通正文默认不额外调整字距。

5. 正文宽度

目标: 避免一行文字过长导致阅读困难。

  • 长正文每行控制在约 60–75 个字符。
  • 不让文章、说明文字随着容器无限变宽。

6. 换行

目标: 标题和正文产生自然、稳定的换行。

  • 标题使用 text-wrap: balance,让多行标题长度更均衡。
  • 短描述使用 text-wrap: pretty,避免最后一行只剩一个短词。
  • URL、ID、长单词使用 overflow-wrap: break-word。
  • Badge、短 Label 等不应换行的内容使用 white-space: nowrap。
  • balance 和 pretty 不用于长篇正文。

7. 动态数字

目标: 数字变化时,文字位置保持稳定。

  • Timer、Counter、Price、Metric 等会变化的数字使用等宽数字。
  • 使用 font-variant-numeric: tabular-nums。
  • 避免数字位宽变化导致界面左右跳动。

8. 文本截断

目标: 空间不足时控制文字长度,但不永久丢失重要内容。

  • 单行文字使用省略号。
  • 多行文字使用 line-clamp。
  • 如果被截断的信息很重要,必须提供查看完整内容的方式。
  • 不重要的信息才可以直接截断。

9. 字体渲染

目标: 避免 macOS 上文字显得过粗。

  • Web 界面可以在全局使用字体抗锯齿:
    -webkit-font-smoothing: antialiased。
  • 应全局设置一次,不在单个组件重复设置。

Color

1. 颜色数量

目标: 控制颜色数量,让界面保持统一。

  • 使用一套中性色,用于背景、文字、边框。
  • 使用一个强调色,用于主要交互和品牌强调。
  • 成功、警告、错误等状态色只在产品确实需要时添加。
  • 不要为了“颜色体系完整”创建实际不会使用的颜色。

2. 同一颜色建立深浅层级

目标: 同一种颜色在不同场景下有稳定的深浅变化。

  • 每个主要颜色建立从浅到深的一组颜色。
  • 每一级必须有明确用途,例如背景、悬停、边框、按钮背景、文字。
  • 不要创建没有实际用途的颜色等级。
  • 同一组颜色应保持基本相同的色相,不要在深浅变化过程中明显变成另一种颜色。

3. 按用途使用颜色

目标: 组件颜色能够统一管理,并随主题一起变化。

  • 不要在组件里直接使用 blue-500、gray-300 这类具体颜色。
  • 先定义颜色用途,例如:
    • 主要文字
    • 次要文字
    • 页面背景
    • 卡片背景
    • 边框
    • 强调色
    • 错误色
  • 组件只使用这些用途颜色。
  • 不要因为两个颜色当前看起来一样,就混用不同用途的颜色。

4. 一种颜色只表达一种含义

目标: 用户看到颜色后能够预测它代表什么。

  • 如果强调色表示“可以点击”,不要同时用于普通静态文字。
  • 如果红色表示错误或危险,不要同时用于普通装饰。
  • 相同含义在不同页面中使用相同颜色。
  • 不同含义不要使用容易混淆的相近颜色。

5. 主要操作的颜色

目标: 用户一眼能找到当前最重要的操作。

  • 实色强调色主要用于最高优先级操作。
  • 一个界面区域通常只保留一个最明显的主要操作。
  • 其他并列操作降低颜色强度,避免多个按钮同时争夺注意力。

6. 深色模式

目标: 深色模式保持与浅色模式一致的信息层级和颜色含义。

  • 不要简单把浅色模式颜色反转。
  • 深色模式需要单独调整亮度、对比度和颜色鲜艳程度。
  • 同一颜色用途在浅色和深色模式下保持相同含义。
  • 整个项目使用统一的主题切换机制。

7. 渐变

目标: 渐变过程自然,不出现脏灰或意外颜色。

  • 普通渐变优先使用 OKLab 插值。
  • 需要保持较高颜色鲜艳度时使用 OKLCH。
  • 避免仅依靠肉眼不断调整中间色来修复不自然的渐变。

Surface

1. 圆角

目标: 不同容器保持统一、自然的圆角关系。

  • 同类组件使用相同圆角。
  • 不要为不同组件随意创建大量圆角值。
  • 小组件使用较小圆角,大容器可以使用更大圆角。
  • 胶囊形按钮、标签等使用完全圆角。

2. 嵌套圆角

目标: 内外两层容器的圆角看起来保持同心。

  • 外层圆角应根据内层圆角和两者之间的间距决定。
  • 可使用:外层圆角 ≈ 内层圆角 + 内边距。
  • 内外圆角不要随意设置成互不相关的数值。

例如:
内层圆角 8px + 间距 8px → 外层圆角约 16px


3. 边框

目标: 只在需要明确边界时使用边框。

  • 边框用于表示结构、分隔、选中、输入区域等明确边界。
  • 如果通过背景或间距已经能区分区域,不再增加边框。
  • 不要默认给所有 Card 和容器加边框。
  • 同一级容器的边框粗细和样式保持一致。

4. 阴影

目标: 用阴影表达元素位于其他内容之上。

  • Dropdown、Popover、Modal、浮动面板等悬浮元素可以使用阴影。
  • 普通静态容器不要仅为了“更精致”而添加明显阴影。
  • 如果只是希望容器从背景中轻微分离,可以使用非常浅的阴影代替明显边框。
  • 避免同时使用强边框和强阴影。

5. 表面层级

目标: 用户能看出哪些内容位于同一层,哪些内容浮在上方。

  • 页面背景、普通容器、浮动容器应形成清晰但克制的层级。
  • 层级越高,不代表颜色或阴影必须越强。
  • 不要通过大量不同背景色制造不必要的层级。
  • 没有明确层级关系的内容应尽量使用相同表面样式。

6. 图片边缘

目标: 无论图片本身是深色还是浅色,都能和背景保持清晰边界。

  • 图片容易与背景融在一起时,增加约 1px 的低透明度内侧描边。
  • 浅色背景使用低透明度深色描边。
  • 深色背景使用低透明度浅色描边。
  • 描边应足够弱,不应成为图片本身的视觉元素。

Icon

1. 图标风格

目标: 所有图标看起来属于同一套系统。

  • 同一产品统一使用 Outline 或 Filled 为主的图标风格。
  • 图标的线宽、圆角、视觉重量保持一致。
  • 不要混用明显不同风格的图标库。
  • 图标颜色默认跟随文字颜色。

2. 图标尺寸

目标: 图标尺寸稳定,不因页面不同随意变化。

  • 使用少量固定尺寸,例如 16px / 20px / 24px。
  • 相同用途的图标保持相同尺寸。
  • 不要出现大量 17px / 19px / 21px 等独立尺寸。

3. 图标与文字

目标: Icon + Text 看起来属于同一个整体。

  • 图标与文字之间保持固定间距。
  • 图标大小应与文字视觉重量匹配。
  • 图标和文字使用视觉对齐,不只依赖数学居中。
  • 非对称图标允许轻微偏移。

4. 图标状态变化

目标: 图标切换时能表达状态之间的关系。

  • Copy → Check、Play → Pause 等状态切换不要默认瞬间替换。
  • 可使用 opacity + scale + blur 做短暂过渡。
  • 动画只用于表达状态变化,不用于纯装饰。

Buttons & Actions

1. 按钮层级

目标: 用户能快速判断哪个操作最重要。

  • 按钮分为 Primary / Secondary / Ghost / Destructive。
  • 一个区域通常只保留一个最明显的 Primary Action。
  • 次要操作不得和主要操作拥有相同视觉权重。
  • 危险操作应和普通操作明确区分。

2. 按钮尺寸

目标: 相同按钮在不同页面保持一致。

  • 使用固定尺寸体系,例如 Small / Medium / Large。
  • 同一尺寸按钮保持相同高度和左右内边距。
  • 按钮宽度默认由内容决定。
  • 不要因为文案不同随意改变按钮高度。

3. 按钮文案

目标: 用户不用猜就知道点击后会发生什么。

  • 优先使用 动词 + 对象。
  • 推荐:Save changes、Create project、Delete account。
  • 避免含义不明确的 OK、Yes、Confirm。
  • 危险操作必须直接说明结果。

4. 图标按钮

目标: 只有图标的按钮仍然容易理解。

  • 只在图标含义足够常见时使用 Icon-only Button。
  • 不明显的图标应提供 Tooltip 或文字说明。
  • 图标按钮保持固定点击区域。
  • 不要因为图标本身很小就缩小整个操作区域。

Form Controls

1. 输入控件一致性

目标: 表单中的不同控件看起来属于同一系统。

  • Input、Select、Textarea 等保持统一高度、圆角、边框和内边距。
  • 同一表单不要混用明显不同的控件风格。
  • 相同尺寸的控件应共享相同高度。

2. Label

目标: 用户始终知道字段要求填写什么。

  • 每个输入项都应有明确 Label。
  • Placeholder 不能代替 Label。
  • Placeholder 只用于示例、格式提示或补充信息。

3. Placeholder

目标: Placeholder 只提供辅助信息。

  • Placeholder 视觉权重低于真实输入内容。
  • 不要在 Placeholder 中放必须记住的信息。
  • 不要只靠 Placeholder 表达字段名称。

4. 错误状态

目标: 用户能快速发现并理解输入错误。

  • 错误状态同时使用颜色和文字提示。
  • 不要只把边框变红。
  • 错误信息放在对应字段附近。
  • 错误文案应说明哪里错了,必要时说明如何修复。

5. Checkbox / Radio / Switch

目标: 不同选择控件表达不同交互含义。

  • Checkbox 用于多选。
  • Radio 用于一组选一。
  • Switch 用于立即生效的开关状态。
  • 不要把这三种控件互相替代。

Navigation

1. 当前页面状态

目标: 用户始终知道自己当前在哪里。

  • 当前页面或当前 Tab 必须有明确状态。
  • Active State 不应只依赖非常轻微的颜色变化。
  • 同一种导航组件使用统一的 Active 表现方式。

2. 导航层级

目标: 用户能理解不同导航项之间的层级关系。

  • 一级和二级导航应通过缩进、字号或间距明确区分。
  • 同一级导航项保持一致布局。
  • 不要创建过多层嵌套导航。

3. Tabs

目标: Tabs 用于同一上下文中的并列内容切换。

  • Tabs 之间应是同一级内容。
  • 当前 Tab 必须明显可见。
  • 不要用 Tabs 表达有上下级关系的页面。
  • Tab 数量过多时不要强行挤在一行。

4. Sidebar

目标: Sidebar 提供稳定的主导航结构。

  • 主导航位置保持稳定。
  • 一级项目和子项目有明确缩进关系。
  • 不要同时使用过多视觉强调。
  • 当前项比 Hover 状态更明显。

5. Breadcrumb

目标: 在层级较深时帮助用户理解当前位置。

  • Breadcrumb 表示真实的页面层级。
  • 当前页面通常不需要可点击。
  • 不要把 Breadcrumb 当作普通标签列表。

Interaction States

1. 必须存在的状态

目标: 用户每次操作都能获得明确反馈。
交互组件按需要提供:

  • Default
  • Hover
  • Focus
  • Pressed
  • Selected
  • Disabled

2. Hover

目标: 鼠标经过时明确提示元素可以操作。

  • Hover 状态应能被看见,但不要过度突出。
  • Hover 不得导致组件尺寸变化。
  • 不要通过增加边框宽度造成 Layout Shift。

3. Focus

目标: 键盘用户能明确看到当前焦点位置。

  • Focus 必须清晰可见。
  • 推荐使用明显的 Focus Ring。
  • Focus 与 Hover、Selected 状态应能区分。
  • 不要直接移除默认 Focus 而不给替代样式。

4. Pressed

目标: 用户点击时获得即时反馈。

  • Pressed 状态应比 Hover 更明确。
  • 可以通过轻微颜色变化或轻微缩放表达。
  • 不要使用夸张位移或缩放。

5. Selected

目标: 用户能明确看出哪些内容已被选择。

  • Selected 状态必须持续可见。
  • 不要只依赖 Hover 表示选中。
  • Selected 和 Disabled 不得使用过于相似的表现方式。

6. Disabled

目标: 用户能看出当前操作不可用。

  • Disabled 元素降低视觉权重。
  • Disabled 不应看起来仍然可以点击。
  • 不要让 Disabled 文本低对比度到难以阅读。

Motion

1. 动画目的

目标: 动画帮助用户理解变化,而不是单纯增加视觉效果。
动画只用于表达:

  • 状态变化
  • 层级变化
  • 空间关系
  • 操作反馈
  • 内容进入或离开

2. 动画时长

目标: 动画足够快,不拖慢操作。

  • 快速反馈约 100–150ms。
  • 普通状态变化约 150–250ms。
  • 较明显的进入动画约 250–400ms。
  • 不要为不同组件随意创建大量动画时长。

3. 可中断动画

目标: 用户改变操作时,动画能够立即响应。

  • Hover、Dropdown、Accordion、Toggle 等交互动画应可中断。
  • 交互状态变化优先使用 Transition。
  • 不要强迫用户等待一个固定动画播放完。

4. 内容进入

目标: 内容出现时能强化阅读顺序。

  • 不要默认让整个大容器一起进入。
  • Heading、Description、Actions 等可以分组进入。
  • 多组内容可以使用轻微 Stagger。
  • Stagger 可从约 100ms 开始。

5. 内容退出

目标: 退出动画快速且不抢注意力。

  • Exit 通常比 Entrance 更短、更轻。
  • 优先使用小幅位移和透明度变化。
  • 避免大距离飞出屏幕。

6. 减少动画

目标: 用户不希望看到动画时仍能正常使用界面。

  • 非必要动画支持 prefers-reduced-motion。
  • 减少动画后不能影响功能理解。

UI Copy

1. 文案清晰

目标: 用户无需解释就能理解界面文字。

  • 优先使用短、直接、具体的表达。
  • 避免为了“有趣”而牺牲清晰度。
  • 不要使用内部术语或用户不了解的缩写。

2. 术语一致

目标: 同一个概念始终使用同一个名称。

  • 如果叫 Workspace,不要在其他地方又叫 Project 或 Space。
  • 同一个操作在不同页面中使用相同文案。
  • 不要为了避免重复而随意换同义词。

3. 操作文案

目标: 用户能预测操作结果。

  • 按钮优先使用动词。
  • 需要时使用 动词 + 对象。
  • 不使用模糊的 OK、Yes、Continue 代替明确操作。

4. 危险操作

目标: 用户明确知道操作会造成什么结果。

  • 使用 Delete project、Remove member、Discard changes。
  • 不使用 Confirm 或 Yes 作为最终危险操作按钮。
  • 危险操作的文案直接描述后果。

5. 错误提示

目标: 用户知道发生了什么,以及下一步怎么办。

  • 错误信息说明具体问题。
  • 能修复时,说明如何修复。
  • 避免只显示 Something went wrong。

6. 链接文案

目标: 用户不用依赖上下文也能理解链接目的。

  • 优先使用 View deployment logs、Read billing documentation。
  • 避免单独使用 Click here、Read more、Learn more。