surface

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

surface

surface

Colour in an interface is mostly the absence of colour. Get the neutrals and the elevation right and one accent will do the rest.
界面中的颜色大多体现为色彩的缺席。先把中性色和层级处理好,一个强调色就足以完成其余设计。

Tokens first

Token 优先

Name colours by role, never by value. A palette that survives both themes has roughly this shape:
TokenRole
--page
The outermost background, behind the app frame
--frame
The app surface
--raised
Cards, popovers, anything lifted one step
--hover
Hover fill for rows and items
--line
Default border
--line-2
Stronger border, focus rings, dividers that must be seen
--fg
Primary text and icons
--fg-2
Secondary text
--fg-3
Tertiary, meta, placeholders
--fg-4
Faint: decorative, disabled, dividers in text
--accent
One. For the primary action and focus, and nothing else
--ok
,
--warn
,
--danger
Status, muted, with a text-safe variant each
Everything else derives. Components never reference a hex; they reference a token. Dark mode is a second set of values for the same tokens, not a second set of components.
颜色应按角色命名,绝不要按具体数值命名。适用于两种主题的色板大致如下:
Token角色
--page
最外层背景,位于应用框架后方
--frame
应用表面
--raised
卡片、弹出层,以及任何抬高一层的内容
--hover
行和项目的悬停填充
--line
默认边框
--line-2
更强的边框、焦点环,以及必须清晰可见的分隔线
--fg
主要文字和图标
--fg-2
次要文字
--fg-3
三级文字、元信息和占位内容
--fg-4
微弱色:装饰内容、禁用状态,以及文字中的分隔线
--accent
仅一个。用于主要操作和焦点状态,不得用于其他位置
--ok
,
--warn
,
--danger
状态色,应保持克制;每种都要配一个适合文字使用的变体
其余颜色全部派生。组件绝不引用十六进制值,只引用 Token。深色模式是同一组 Token 的第二套取值,而不是第二套组件。

Neutrals

中性色

Near-black and near-white, both slightly warm or slightly cool, consistently. Pure
#000
and
#fff
are too hard; a dark theme on
#080808
with text at
#ededed
reads as considered where
#000
/
#fff
reads as unset.
A workable dark set:
page #080808 · frame #0d0d0d · raised #141414 · hover #161616
line #1c1c1c · line-2 #262626
fg #ededed · fg-2 #9b9b9b · fg-3 #6b6b6b · fg-4 #454545
And light:
page #f2f2f0 · frame #fbfbfa · raised #ffffff · hover #f1f1ef
line #ebebe8 · line-2 #dcdcd8
fg #151515 · fg-2 #5f5f5c · fg-3 #8d8d89 · fg-4 #b8b8b3
Steps are small on purpose. Surfaces differ by 2–4% lightness; that is enough for the eye to separate them without stripes. Test by taking a screenshot and squinting: you should see the structure, not a patchwork.
Build neutrals in OKLCH when generating them, so that steps are perceptually even and the hue stays constant as lightness changes.
接近黑色和接近白色,两者都要略偏暖或略偏冷,并保持一致。纯
#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,这样各色阶在感知上更加均匀,且随着亮度变化,色相保持不变。

Dark mode is not inverted light mode

深色模式不是反转的浅色模式

  • Light mode lifts things with shadows. Dark mode lifts things with lighter surfaces and borders. A shadow on
    #0d0d0d
    is invisible.
  • Dark surfaces get a 1px inset top highlight at 4% white to read as lit from above:
    box-shadow: 0 1px 0 rgba(255,255,255,.04) inset
    .
  • Saturated colours glow on dark. Desaturate the accent 10–20% and lower its lightness for dark mode.
  • Pure white text on pure black vibrates. Lower both.
  • Images and illustrations may need a subtle dim (
    opacity: .9
    ) or a dark-mode variant.
  • color-scheme: dark
    on the root so form controls, scrollbars and the browser UI match.
  • 浅色模式通过阴影抬起元素。深色模式则通过更亮的表面和边框来建立层级。在
    #0d0d0d
    上使用阴影是看不见的。
  • 深色表面增加 1px 内嵌顶部高光,并使用 4% 白色,使其看起来像被上方光源照亮:
    box-shadow: 0 1px 0 rgba(255,255,255,.04) inset
    。
  • 饱和颜色在深色背景上会发光。深色模式下将强调色降低 10–20% 饱和度,并降低其亮度。
  • 纯白文字配纯黑背景会产生视觉颤动。不要使用纯白和纯黑,应适当降低二者的对比反差。
  • 图片和插图可能需要轻微降低亮度(
    opacity: .9
    ),或使用深色模式变体。
  • 在 root 上设置
    color-scheme: dark
    ,使表单控件、滚动条和浏览器 UI 与主题保持一致。

Borders vs shadows

边框与阴影

ThemeCard / popover edge
Dark
border: 1px solid var(--line-2)
+ inset highlight; a soft outer shadow only for floating layers:
0 12px 40px -12px rgba(0,0,0,.9)
Light
box-shadow: 0 1px 0 rgba(255,255,255,.9) inset, 0 12px 32px -14px rgba(0,0,0,.18)
; border only where the surface sits on the same colour
Shadows are one layer, soft, with negative spread, in the direction of a single light source. Never
0 0 20px
(a glow), never multiple stacked blurs, never a coloured shadow in product UI.
Borders are 1px. Not 0.5px (it renders inconsistently), not 2px unless it is a focus ring.
主题卡片或浮层边缘
深色
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
(光晕),不要叠加多层模糊,也不要在产品 UI 中使用彩色阴影。
边框为 1px。不要使用 0.5px(渲染不一致),除非是焦点环,否则也不要使用 2px。

Alpha

Alpha

Overlays, hover fills and dividers on unknown backgrounds use alpha, so they work on any surface:
  • Hover fill: foreground at 4–6%
  • Pressed fill: foreground at 8–10%
  • Divider: foreground at 8–12%
  • Backdrop behind dialogs: page colour at 60–70%, with
    backdrop-filter: blur(4px)
    at most
  • Glass surfaces (
    backdrop-filter
    ) are expensive; one per screen, never animated, never nested
在未知背景上,叠加层、悬停填充和分隔线使用 Alpha,以便适配任何表面:
  • 悬停填充:前景色的 4–6%
  • 按下填充:前景色的 8–10%
  • 分隔线:前景色的 8–12%
  • 对话框背后的遮罩:页面颜色的 60–70%,并最多使用
    backdrop-filter: blur(4px)
  • Glass 表面(
    backdrop-filter
    )的渲染成本较高;每个屏幕最多使用一个,不要制作动画,也不要相互嵌套

The accent

强调色

One hue. It goes on the primary button, the focus ring if the neutral ring is too quiet, links in running text, and the active state of a control. It does not go on icons, headings, borders, backgrounds or badges.
If everything is the accent, nothing is. Count the accent-coloured elements on a screen; if it is more than three, remove some.
Status colours are muted (a green at ~55% chroma, not a traffic light) and always paired with a word or icon. Colour is never the only signal.
只使用一种色相。它用于主按钮、当中性色焦点环过于低调时替代焦点环、正文中正在阅读的链接,以及控件的激活状态。不会用于图标、标题、边框、背景或徽章。
如果所有元素都是强调色,就等于没有强调色。统计屏幕中使用强调色的元素;如果超过三个,就移除其中一些。
状态色应保持克制(绿色的彩度约为 55%,而不是交通灯色),并且始终搭配文字或图标。颜色绝不能成为唯一的信号。

Contrast

对比度

  • Body and secondary text: 4.5:1 against its surface.
  • Large text (18px+ or 14px+ bold) and UI boundaries (icons, borders on inputs): 3:1.
  • Tertiary text is allowed to sit at 3:1 only when it is not needed to use the interface.
  • Faint (
    --fg-4
    ) is decoration. Never put information in it.
  • Check both themes. A grey that passes on white will fail on
    #0d0d0d
    .
Placeholders count as text if they carry the only label; then they must pass. Better: always have a visible label.
  • 正文和次要文字:与其表面对比度达到 4.5:1。
  • 大号文字(18px+ 或 14px+ 粗体)和 UI 边界(图标、输入框边框):达到 3:1。
  • 三级文字仅在不影响界面使用的情况下,才可降至 3:1。
  • 微弱色(
    --fg-4
    )仅用于装饰,绝不能承载信息。
  • 两种主题都要检查。在白色上通过的灰色,在
    #0d0d0d
    上就会失败。
如果占位文本(placeholder)是唯一的标签,就应按文字处理,因此必须通过对比度要求。更好的做法是始终提供可见标签。

Gradients, noise, texture

渐变、噪点与纹理

Product UI: none, or one very quiet radial behind a hero region at 2% opacity to give the page a light source. Marketing: allowed, and still one.
Noise or dither over a gradient hides banding and adds material; keep it at 3–6% opacity and tile a small SVG rather than a large PNG.
A backdrop pattern (grid, ticks, dots) at the
--line
level sits behind the frame and stops at its edge; it never runs under content.
产品 UI:通常不使用;若要使用,仅可在 hero 区域背后放置一个非常克制的径向渐变,以 2% 不透明度营造页面受光感。营销页面:可以使用,但仍然只保留一个。
在渐变上叠加噪点或抖动可以隐藏色带并增加材质感;不透明度保持在 3–6%,并平铺小型 SVG,而不是大型 PNG。
背景图案(网格、刻度、圆点)使用
--line
级别,并位于框架后方且在框架边缘处停止;绝不能延伸到内容下方。

Selection, focus, scrollbars

选区、焦点与滚动条

css
::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; }
These three lines are the difference between a themed app and a page with a theme applied to it.
css
::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; }
这三行代码,是真正具有主题感的应用与仅套用了主题的页面之间的区别。

Theme switching

主题切换

  • Decide the theme before first paint with an inline script reading storage and
    prefers-color-scheme
    . A flash of the wrong theme is a bug.
  • Store the choice; respect the system when there is no choice.
  • Transition
    background-color
    on the root over ~400ms so the switch is not a strobe. Or use a View Transition to reveal the new theme from the toggle; either way, under reduced motion it is instant.
  • Both themes are first-class. If the team only ever looks at dark, light will rot, and vice versa. Review in both.
  • 在首次绘制前,使用内联脚本读取存储和
    prefers-color-scheme
    ,提前确定主题。错误主题闪现属于 bug。
  • 保存用户的选择;用户未选择时遵循系统设置。
  • 在 root 上用约 400ms 过渡
    background-color
    ,避免切换时产生频闪。也可以使用 View Transition,从切换按钮处揭示新主题;无论采用哪种方式,在减少动态效果(reduced motion)模式下都应立即完成切换。
  • 两种主题同等重要。如果团队只查看深色主题,浅色主题就会逐渐失修,反之亦然。两种模式都要审查。

Marks of a cheap surface

表面显得廉价的迹象

SignFix
#000
page,
#fff
text
#080808
/
#ededed
Cards with border and shadow and a different backgroundone of the three, two at most
Glow shadows (
0 0 24px accent
)
soft directional shadow or none
Every card a different tintone surface token
Accent on icons and headingsneutrals; accent on the one action
Grey text at 2.5:1 "because it looks lighter"
--fg-2
that passes; use size and weight for hierarchy
Gradient buttonsflat fill, border-only secondary, ghost tertiary
Dark mode with the same shadows as lightborders and inset highlights
迹象修正方式
#000
页面、
#fff
文本
#080808
/
#ededed
同时具有边框、阴影和不同背景的卡片三者择一,最多使用两种
发光阴影(
0 0 24px accent
)
柔和的方向性阴影,或不使用阴影
每张卡片使用不同色调使用一个表面 Token
图标和标题使用强调色使用中性色;仅在唯一的主要操作上使用强调色
2.5:1 的灰色文字“因为看起来更浅”使用符合对比度要求的
--fg-2
;通过字号和字重建立层级
渐变按钮主按钮使用纯色填充,次按钮仅用边框,第三级按钮使用 ghost 样式
深色模式沿用浅色模式的阴影使用边框和内嵌高光

From the catalogue

资源目录