关于 surface
surface 是用于设计界面颜色、表面层级和明暗主题的设计技能。它为产品界面提供语义令牌、对比度、强调色和主题切换方面的指导。
主要功能
- 01定义语义颜色令牌
- 02构建协调的明暗主题调色板
- 03设定表面层级与边框
- 04规范对比度和状态颜色
- 05配置主题切换与焦点样式
适用场景
- 01创建支持明暗主题的配色系统
- 02检查文本和控件对比度
- 03统一卡片与弹出层表面样式
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ paragonhq/ stealth-skills --skill surface安装后在 Claude Code 中输入/surface确认已加载。
详细介绍
表面
界面中的颜色大多意味着没有颜色。把中性色和层级关系处理好,一种强调色就足够了。
先定义令牌
按用途命名颜色,绝不要按数值命名。能适用于两种主题的调色板大致如下:
| 令牌 | 用途 |
|---|---|
--page | 最外层背景,位于应用框架之后 |
--frame | 应用表面 |
--raised | 卡片、弹出层以及所有高出一层的元素 |
--hover | 行和项目的悬停填充色 |
--line | 默认边框 |
--line-2 | 更醒目的边框、焦点环以及必须清晰可见的分隔线 |
--fg | 主要文本和图标 |
--fg-2 | 次要文本 |
--fg-3 | 三级文本、元信息、占位符 |
--fg-4 | 微弱元素:装饰、禁用状态、文本中的分隔线 |
--accent | 只用一种。用于主要操作和焦点,其他地方一概不用 |
--ok、--warn、--danger | 状态色,采用柔和色调,并分别提供适用于文本的变体 |
其他一切都由此派生。组件绝不直接引用十六进制颜色,而是引用令牌。深色模式是同一组令牌的第二套取值,而不是第二套组件。
中性色
始终使用略带暖调或冷调的近黑色和近白色。纯 #000 和 #fff 过于生硬;深色主题采用 #080808 搭配 #ededed 的文本,会显得经过精心设计,而 #000/#fff 则像是尚未设置。
一套可行的深色值:
page #080808 · frame #0d0d0d · raised #141414 · hover #161616
line #1c1c1c · line-2 #262626
fg #ededed · fg-2 #9b9b9b · fg-3 #6b6b6b · fg-4 #454545
浅色值:
page #f2f2f0 · frame #fbfbfa · raised #ffffff · hover #f1f1ef
line #ebebe8 · line-2 #dcdcd8
fg #151515 · fg-2 #5f5f5c · fg-3 #8d8d89 · fg-4 #b8b8b3
色阶有意设置得很细微。表面的亮度差异为 2–4%,足以让眼睛区分它们,又不会显得条纹分明。截屏后眯起眼睛观察:你应该看到结构,而不是杂乱的色块。
生成中性色时,使用 OKLCH 来构建,这样色阶在视觉上均匀,且色相不会随着亮度变化而改变。
深色模式不是反转后的浅色模式
- 浅色模式用阴影提升元素。深色模式用更亮的表面和边框提升元素。在
#0d0d0d上,阴影不可见。 - 深色表面的顶部添加 1px 内嵌高光,白色不透明度为 4%,让表面看起来像是从上方受光:
box-shadow: 0 1px 0 rgba(255,255,255,.04) inset。 - 高饱和度的颜色在深色背景上会发光。深色模式下,将强调色的饱和度降低 10–20%,并降低其亮度。
- 纯黑上的纯白文本会刺眼。两者都应调柔和。
- 图片和插画可能需要轻微变暗(
opacity: .9)或提供深色模式专用版本。 - 在根元素上设置
color-scheme: dark,让表单控件、滚动条和浏览器界面保持一致。
边框与阴影
| 主题 | 卡片 / 弹出层边缘 |
|---|---|
| 深色 | border: 1px solid var(--line-2) + 内嵌高光;只有浮动层使用柔和的外阴影:0 12px 40px -12px rgba(0,0,0,.9) |
| 浅色 | box-shadow: 0 1px 0 rgba(255,255,255,.9) inset, 0 12px 32px -14px rgba(0,0,0,.18);只有表面与其所在背景颜色相同时才使用边框 |
阴影只用一层,柔和且带负扩展,方向与单一光源一致。绝不用 0 0 20px(光晕),绝不用多层模糊叠加,也绝不在产品界面中使用彩色阴影。
边框为 1px。不要用 0.5px(渲染效果不一致),除焦点环外也不要用 2px。
Alpha
叠加层、悬停填充色和未知背景上的分隔线使用 Alpha 值,这样它们能适用于任何表面:
- 悬停填充:前景色不透明度为 4–6%
- 按下填充:前景色不透明度为 8–10%
- 分隔线:前景色不透明度为 8–12%
- 对话框后方的背景遮罩:页面颜色不透明度为 60–70%,并且
backdrop-filter: blur(4px)最多只用到这个程度 - 玻璃表面(
backdrop-filter)开销较大;每个屏幕最多用一个,不要动画,也不要嵌套
强调色
只用一种色相。它用于主要按钮;当中性色焦点环不够醒目时,也用于焦点环;还用于行内文本链接和控件的激活状态。不要用于图标、标题、边框、背景或徽章。
如果所有东西都是强调色,那就等于没有强调色。数一下屏幕上使用强调色的元素;如果超过三个,就删掉一些。
状态色应柔和(绿色的色度约为 55%,而不是交通信号灯般鲜艳),并且始终搭配文字或图标。颜色绝不是唯一的提示方式。
对比度
- 正文和次要文本:与其表面的对比度至少为 4.5:1。
- 大号文本(18px 以上或 14px 以上的粗体文本)和 UI 边界(图标、输入框边框):对比度至少为 3:1。
- 只有在使用界面时不需要依赖三级文本的情况下,其对比度才可以低至 3:1。
- 淡显(
--fg-4)仅用于装饰,绝不能承载信息。 - 两种主题都要检查。在白色背景上通过的灰色,在
#0d0d0d上可能就无法通过。
如果占位符是唯一的标签,就要将其视为文本;此时必须达到对比度要求。更好的做法是始终显示可见标签。
渐变、噪点、纹理
产品 UI:不使用渐变,或者只在主视觉区域背后使用一个非常柔和的径向渐变,以 2% 的不透明度为页面营造光源效果。营销页面:可以使用,但仍只用一个。
渐变上叠加噪点或抖动纹理,可以隐藏色带并增加质感;不透明度保持在 3–6%,并使用平铺的小型 SVG,而不是大型 PNG。
背景图案(网格、刻度、点)采用 --line 级别的颜色,置于框架后方并在边缘处停止;绝不能延伸到内容下方。
选中状态、焦点、滚动条
::selection { background: var(--fg); color: var(--frame); }
:focus-visible { outline: 1px solid var(--fg-2); outline-offset: 2px; }
* { scrollbar-width: thin; scrollbar-color: var(--line-2) transparent; }
这三处决定了应用是经过主题设计的,还是仅仅套用了主题。
切换主题
- 在首次绘制前,通过内联脚本读取存储值和
prefers-color-scheme来确定主题。错误主题一闪而过也是 Bug。 - 保存用户的选择;如果用户没有选择,则遵循系统设置。
- 在根元素上对
background-color使用约 400ms 的过渡效果,避免切换时闪烁。或者使用视图过渡,从切换控件处揭示新主题;无论采用哪种方式,在减少动态效果模式下都应立即切换。 - 两种主题都应得到充分支持。如果团队只关注深色主题,浅色主题就会逐渐失修,反之亦然。两种主题都要检查。
廉价表面的特征
| 特征 | 改进方法 |
|---|---|
#000 页面、#fff 文本 | 使用 #080808 / #ededed |
| 卡片同时有边框、阴影和不同的背景色 | 三者选一,最多选两种 |
发光阴影(0 0 24px accent) | 使用柔和的定向阴影,或不加阴影 |
| 每张卡片都使用不同的色调 | 使用同一个表面令牌 |
| 灰色文本的对比度为 2.5:1,“因为看起来更浅” | 使用通过对比度检查的 --fg-2;通过字号和字重体现层级 |
| 渐变按钮 | 使用纯色填充;次要按钮仅使用边框,三级按钮使用幽灵样式 |
| 深色模式沿用浅色模式的阴影 | 使用边框和内侧高光 |
目录精选
- Radix Colors — 为每种用途提供定义明确的色阶,并支持两种主题。
- Open Props — 提供合理的阴影和中性色默认值。
- Realtime Colors — 在真实布局中预览调色板,再决定是否采用。
- 对比度检查工具 — 趁别人检查之前先自行检查。
- Dark Mode Design — 展示深色模式设计出色的网站。
- fffuel — SVG 噪点和颗粒纹理生成器。
- 更多资源见 stealth.pm/assets。