designing-frontend-interfaces

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Designing Frontend Interfaces

前端界面设计

Overview

概述

Distinctive interfaces come from committing to one aesthetic direction and executing it with a locked system of values. Generic interfaces come from picking each value ad hoc at the moment it is needed.
Core principle: Decide the system before you write the components. Every number in the finished CSS should trace back to a scale you chose deliberately.
This skill covers visual craft. Two companion skills cover the rest of interface quality:
  • REQUIRED SUB-SKILL for anything with flows, forms, or non-trivial state: use
    designing-user-experience
  • REQUIRED SUB-SKILL before shipping: use
    building-accessible-interfaces
  • To critique a finished UI, use
    reviewing-interface-quality
独特的界面源于坚定遵循一种美学方向,并通过一套固定的价值体系来执行。通用化界面则是在需要时随意选择各项设计值。
核心原则: 在编写组件前先确定设计体系。最终CSS中的每一个数值都必须追溯到你刻意选择的比例体系。
本技能涵盖视觉工艺。另外两个配套技能覆盖了界面质量的其他方面:
  • 对于包含流程、表单或复杂状态的任何项目,必须配套使用
    designing-user-experience
    技能
  • 上线前必须配套使用
    building-accessible-interfaces
    技能
  • 如需评估已完成的UI,使用
    reviewing-interface-quality
    技能

The Iron Law

铁律

NO COMPONENT CODE BEFORE THE DESIGN BRIEF AND TOKEN BLOCK EXIST
The brief is 8 lines. The token block is one CSS block. Together they cost about ninety seconds and they are what separates a designed interface from a decorated one.
Wrote components first? The values are already arbitrary. Extract them into a real scale before continuing — do not rationalize the numbers you happened to type.
NO COMPONENT CODE BEFORE THE DESIGN BRIEF AND TOKEN BLOCK EXIST
设计brief只需8行内容,令牌块是一个CSS代码块。两者总共耗时约90秒,却是区分“经过设计的界面”和“仅做装饰的界面”的关键。
如果已经先写了组件代码?那其中的数值已经是随意设定的了。在继续开发前,将这些数值提取为一套真正的比例体系——不要为你随手输入的数字找理由。

Checklist

检查清单

Create a task for each item and complete them in order:
  1. Write the design brief — 8 lines, in your response, before any file is created
  2. Choose an aesthetic direction — from
    references/aesthetic-directions.md
    , or invent one, and name it
  3. Lock the token block — type scale, color ramp, space scale, radius, shadow, motion. One CSS block.
  4. Build the layout skeleton — structure and spacing, no decoration yet
  5. Build components — every interactive element gets all five states
  6. Add the one memorable moment — the single thing someone will remember
  7. Self-review against the rubric — see Self-Review below; fix what fails
  8. Verify visually — render it and actually look at it
为每个任务创建条目并按顺序完成:
  1. 撰写设计brief——在创建任何文件前,先在回复中写出8行内容
  2. 选择美学方向——从
    references/aesthetic-directions.md
    中选择,或自行创造并命名
  3. 锁定令牌块——字体比例、色彩梯度、间距比例、圆角、阴影、动效。用一个CSS代码块定义。
  4. 构建布局骨架——先搭建结构和设置间距,暂不添加装饰
  5. 构建组件——每个交互元素都要包含五种状态
  6. 添加一个记忆点——让用户能记住的一个独特设计
  7. 对照评分标准自我审查——查看下方的自我审查部分;修正不符合要求的内容
  8. 视觉验证——渲染界面并实际查看效果

Step 1: The Design Brief

步骤1:设计brief

Write this out before creating any file. It is 8 lines of plain text in your response — not a document, not a file.
Purpose:    What job does this interface do?
Audience:   Who uses it, in what context, on what device?
Tone:       One named direction (see references/aesthetic-directions.md)
Reference:  A real-world visual analogue — a magazine, a era, a discipline
Palette:    Base / surface / ink / one accent — as hue intentions, not hex yet
Type:       Display face + text face, and why this pairing
Memorable:  The one thing someone will describe to a friend
Restraint:  What this design deliberately does NOT do
The
Restraint
line matters most. A design with no stated restraint becomes a design with every effect applied at once.
Worked example:
Purpose:    Changelog for a developer infrastructure tool
Audience:   Engineers skimming on a wide monitor, 30 seconds per visit
Tone:       Technical Broadsheet — newspaper density meets terminal precision
Reference:  Financial Times print edition, 1990s man pages
Palette:    Warm newsprint base, near-black ink, one signal red for breaking changes
Type:       Instrument Serif display / IBM Plex Mono text — editorial authority, machine detail
Memorable:  Version numbers set enormous in the left margin, hanging outside the text column
Restraint:  No cards. No shadows. No rounded corners. Rules and space only.
在创建任何文件前先写出内容。只需在回复中写8行纯文本——无需文档或文件。
Purpose:    该界面的用途是什么?
Audience:   谁会使用它?使用场景和设备是什么?
Tone:       一个明确命名的美学方向(参考references/aesthetic-directions.md)
Reference:  现实世界中的视觉参考物——杂志、某个时代、某类设计风格
Palette:    基础色/表面色/文字色/一个强调色——仅说明色调意向,暂不用十六进制值
Type:       标题字体+正文字体,以及选择该组合的理由
Memorable:  用户会向朋友描述的一个设计点
Restraint:  该设计刻意不做的内容
Restraint
这一行最为重要。没有明确约束的设计,最终会变成堆砌所有效果的杂乱界面。
示例:
Purpose:    开发者基础设施工具的更新日志
Audience:   工程师在宽屏显示器上快速浏览,每次访问耗时约30秒
Tone:       技术简报——报纸的信息密度结合终端的精准感
Reference:  《金融时报》印刷版、90年代的手册页面
Palette:    暖调新闻纸底色、近乎黑色的文字色、用于重大变更的信号红强调色
Type:       Instrument Serif标题字体 / IBM Plex Mono正文字体——兼具编辑权威性和机器细节感
Memorable:  版本号在左侧边栏设置为超大字号,超出文本列范围
Restraint:  不使用卡片、不添加阴影、不设置圆角,仅用线条和间距区分内容

Step 2: Lock the Token Block

步骤2:锁定令牌块

Every value the interface uses comes from here. If a number is not in this block, it does not belong in a component.
css
:root {
  /* TYPE — one ratio, applied consistently. 1.200 minor third (dense UI),
     1.250 major third (general), 1.333 perfect fourth (marketing),
     1.500 perfect fifth (editorial/poster). Pick ONE. */
  --font-display: "Instrument Serif", Georgia, serif;
  --font-text:    "IBM Plex Mono", ui-monospace, monospace;
  --step--1: 0.833rem;  --step-0: 1rem;     --step-1: 1.25rem;
  --step-2:  1.563rem;  --step-3: 1.953rem; --step-4: 2.441rem;
  --step-5:  3.052rem;  --step-6: 3.815rem;

  /* SPACE — one base unit and its multiples. Never a value in between. */
  --space-3xs: 0.25rem; --space-2xs: 0.5rem; --space-xs: 0.75rem;
  --space-s:   1rem;    --space-m:   1.5rem; --space-l:  2rem;
  --space-xl:  3rem;    --space-2xl: 4.5rem; --space-3xl: 7rem;

  /* COLOR — a ramp, not a set of one-offs. One accent, used sparingly. */
  --base:    #f4f1ea;   /* page */
  --surface: #ffffff;   /* raised */
  --ink:     #12100e;   /* primary text */
  --ink-2:   #55504a;   /* secondary text */
  --ink-3:   #8b857c;   /* tertiary / meta */
  --line:    #ddd7cc;   /* borders, rules */
  --accent:  #c1362f;   /* <10% of visible surface */

  /* FORM */
  --radius: 0;          /* commit: 0, or 2-4px, or a full 999px pill. Not "some of each". */
  --border: 1px solid var(--line);
  --shadow: none;       /* if using shadows, define 2 — near and far. Never 5. */

  /* MOTION */
  --dur-fast: 120ms; --dur: 220ms; --dur-slow: 420ms;
  --ease:     cubic-bezier(0.2, 0, 0, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}
Rules for the block:
TokenRule
Type scaleOne ratio for the whole page. Deriving sizes by eye produces muddy hierarchy.
Space scaleEvery margin/padding/gap is a
--space-*
token. No
13px
. No
0.85rem
.
Color rampThree ink levels is enough. A fourth means the hierarchy is unclear.
AccentExactly one, unless the direction is explicitly maximalist. Under 10% of surface.
RadiusOne value. Mixed radii is the single loudest amateur signal.
ShadowZero or two. Two shadows = one tight contact shadow + one wide ambient.
MotionThree durations, two easings. That covers every transition an interface needs.
界面使用的所有数值都来自这里。如果某个数值不在此块中,就不应该出现在组件里。
css
:root {
  /* TYPE — 统一使用一个比例。1.200为小三度(紧凑UI),
     1.250为大三度(通用场景),1.333为纯四度(营销场景),
     1.500为纯五度(编辑/海报场景)。只选一个。 */
  --font-display: "Instrument Serif", Georgia, serif;
  --font-text:    "IBM Plex Mono", ui-monospace, monospace;
  --step--1: 0.833rem;  --step-0: 1rem;     --step-1: 1.25rem;
  --step-2:  1.563rem;  --step-3: 1.953rem; --step-4: 2.441rem;
  --step-5:  3.052rem;  --step-6: 3.815rem;

  /* SPACE — 一个基础单位及其倍数。绝不使用中间值。 */
  --space-3xs: 0.25rem; --space-2xs: 0.5rem; --space-xs: 0.75rem;
  --space-s:   1rem;    --space-m:   1.5rem; --space-l:  2rem;
  --space-xl:  3rem;    --space-2xl: 4.5rem; --space-3xl: 7rem;

  /* COLOR — 一套梯度色彩,而非零散的颜色。仅用一个强调色,且使用要克制。 */
  --base:    #f4f1ea;   /* 页面底色 */
  --surface: #ffffff;   /* 凸起元素底色 */
  --ink:     #12100e;   /* 主文字色 */
  --ink-2:   #55504a;   /* 次要文字色 */
  --ink-3:   #8b857c;   /*  tertiary / meta */
  --line:    #ddd7cc;   /* 边框、分隔线 */
  --accent:  #c1362f;   /* 占可见区域<10% */

  /* FORM */
  --radius: 0;          /* 确定值:0,或2-4px,或完整的999px胶囊型。不要“混用”。 */
  --border: 1px solid var(--line);
  --shadow: none;       /* 如果使用阴影,定义2种——近阴影和远阴影。绝不使用5种。 */

  /* MOTION */
  --dur-fast: 120ms; --dur: 220ms; --dur-slow: 420ms;
  --ease:     cubic-bezier(0.2, 0, 0, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}
令牌块规则:
令牌类型规则
字体比例整页使用同一个比例。凭视觉调整字号会导致层级混乱。
间距比例所有边距/内边距/间隙都使用
--space-*
令牌。禁止使用
13px
0.85rem
这类值。
色彩梯度三种文字色足够。四种则说明层级不清晰。
强调色仅用一种,除非明确选择了极繁主义方向。占可见区域比例低于10%。
圆角统一使用一个值。混合不同圆角是最明显的业余设计信号。
阴影0种或2种。两种阴影=一种紧凑的接触阴影+一种宽泛的环境阴影。
动效三种时长,两种缓动函数。这足以覆盖界面所需的所有过渡效果。

Step 3: Hierarchy

步骤3:层级设计

Vary one dimension at a time. Amateur hierarchy changes size and weight and color and spacing simultaneously; everything shouts and nothing leads.
NeedChangeKeep fixed
Section heading vs. bodySize (2+ steps)Weight, color
Emphasis inside a paragraphWeightSize, color
Metadata / captionsColor (to
--ink-3
)
Size, weight
GroupingSpace (a full step)Everything else
Space groups more reliably than lines or boxes. Before adding a border or a card, try doubling the gap between groups and halving it within them. Most "needs a card" instincts are actually "needs correct spacing."
一次只改变一个维度。 业余的层级设计会同时改变字号、字重、颜色和间距;所有元素都在“呐喊”,却没有引导性。
需求调整项保持固定的项
章节标题 vs 正文字号(相差2级以上)字重、颜色
段落内强调内容字重字号、颜色
元数据/说明文字颜色(改为
--ink-3
字号、字重
内容分组间距(完整一级)其他所有项
间距比线条或框更可靠地实现分组。 在添加边框或卡片前,尝试将组间间隙加倍,组内间隙减半。大多数“需要卡片”的直觉实际上是“需要正确的间距”。

Step 4: Anti-Slop

步骤4:避免AI通用化陷阱

Generic AI interface output has a specific, enumerable signature. These are not stylistic preferences — they are the exact defaults that make output identifiable on sight.
NeverInstead
Inter, Roboto, Arial,
system-ui
as the display face
A face with an opinion — see
references/typography.md
Purple/violet → blue gradient on whiteCommit to a real palette from a real reference
rounded-lg
/
rounded-xl
on everything
One radius, chosen; or zero radius
A
shadow-md
on every surface
Shadows only where something genuinely floats
Hero → 3 feature cards → CTALet the content decide the structure
A centered
max-w-4xl
column for the whole page
Vary measure by content type; break the grid at least once
text-gray-500
body text on white
Real ink colors with intent; body text at full contrast
Emoji standing in for iconsA real icon set, or no icons
Six weights of one neutral sansTwo faces, three weights, maximum
Every section the same vertical rhythmVary section spacing to express importance
The convergence trap: across separate generations, do not keep reaching for the same "safe distinctive" choices. Space Grotesk, a dark slate background, and a lime-green accent is now its own cliché. Vary light and dark, serif and sans and mono, dense and airy, across designs. Check
references/aesthetic-directions.md
and pick one you did not pick last time.
Rationalization table:
ThoughtReality
"The user didn't specify a style, so neutral is safest"Unspecified means you choose. Neutral is a choice, and it is the worst one.
"It's just a quick demo"Demos are what people look at. The brief takes ninety seconds.
"Inter is genuinely a good typeface"It is. It is also the single strongest generic-AI signal. Use it for body text at most, never for display.
"A card grid is the clearest way to show features"It is the most common way. Try a table, a list with rules, a stepped layout, an editorial spread.
"I'll add the distinctive touches at the end"Distinctiveness is structural. Bolted on at the end it reads as decoration.
"Tailwind defaults are a design system"They are a palette of defaults. Configure them, or you ship the defaults everyone ships.
通用AI生成的界面有特定的可识别特征。这些不是风格偏好——正是这些默认设置让AI生成的界面一眼就能被认出。
禁止做法替代方案
将Inter、Roboto、Arial、
system-ui
用作标题字体
使用有独特风格的字体——参考
references/typography.md
在白色背景上使用紫→蓝渐变从真实参考物中选用一套真正的配色方案
所有元素都使用
rounded-lg
/
rounded-xl
圆角
选择一个统一的圆角值,或不使用圆角
每个表面都添加
shadow-md
阴影
仅在元素真正需要悬浮时添加阴影
英雄区→3个功能卡片→CTA的固定结构让内容决定布局结构
整页使用居中的
max-w-4xl
根据内容类型调整宽度;至少打破一次网格布局
在白色背景上使用
text-gray-500
正文
使用有明确意图的真实文字颜色;正文使用全对比度
用表情符号替代图标使用一套真正的图标集,或不使用图标
使用一种中性无衬线字体的6种字重最多使用两种字体、三种字重
所有章节使用相同的垂直间距调整章节间距以体现内容重要性
趋同陷阱: 在不同项目中,不要一直选择同样的“安全且独特”的选项。Space Grotesk字体、深灰背景、石灰绿强调色现在已经成了新的陈词滥调。在不同设计中切换明暗、衬线/无衬线/等宽字体、紧凑/宽松布局。查看
references/aesthetic-directions.md
并选择一个你上次没选的方向。
合理化误区对照表:
想法实际情况
“用户没有指定风格,所以中性风格最安全”未指定意味着由你选择。中性是一种选择,而且是最糟糕的选择。
“这只是一个快速演示”演示是人们关注的重点。写brief只需要90秒。
“Inter确实是一款好字体”没错。但它也是AI生成界面最强烈的信号。最多将其用于正文,绝不要用作标题。
“卡片网格是展示功能最清晰的方式”这是最常见的方式。试试表格、带线条的列表、阶梯式布局、编辑式排版。
“我会在最后添加独特的设计细节”独特性是结构性的。最后添加的细节只会被视为装饰。
“Tailwind默认值就是一套设计系统”它们只是一套默认值的集合。你需要配置它们,否则你就会和所有人一样使用默认值。

Step 5: Component States

步骤5:组件状态

Every interactive element gets all five. Missing states are the most common gap in generated UI.
css
.btn              { /* rest */ }
.btn:hover        { /* pointer only — must not be the sole affordance */ }
.btn:active       { /* pressed — a real transform, not just a color shift */ }
.btn:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
.btn:disabled     { /* visibly inert, and still readable */ }
.btn[aria-busy]   { /* loading — reserve the space, don't collapse the layout */ }
Never remove focus outlines. If the default outline is ugly, restyle it — do not set
outline: none
. See
building-accessible-interfaces
.
每个交互元素都要包含以下五种状态。缺失状态是生成式UI最常见的漏洞。
css
.btn              { /* 基础样式 */ }
.btn:hover        { /* 仅鼠标悬停状态——不能作为唯一的交互提示 */ }
.btn:active       { /* 按下状态——使用真实的变换效果,不要只改颜色 */ }
.btn:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
.btn:disabled     { /* 视觉上呈现不可用状态,且仍可读 */ }
.btn[aria-busy]   { /* 加载状态——保留空间,不要让布局塌陷 */ }
绝不要移除焦点轮廓。如果默认轮廓不好看,就重新设计它——不要设置
outline: none
。参考
building-accessible-interfaces
技能。

Step 6: Verify Visually

步骤6:视觉验证

Rendering code and reading it back is not verification. Look at the actual pixels.
bash
undefined
只看代码不渲染不是验证。要查看实际的像素效果。
bash
undefined

Static HTML

静态HTML

python3 -c " from playwright.sync_api import sync_playwright with sync_playwright() as p: b = p.chromium.launch() pg = b.new_page(viewport={'width':1440,'height':900}) pg.goto('file:///abs/path/index.html'); pg.wait_for_timeout(600) pg.screenshot(path='/tmp/desktop.png', full_page=True) pg.set_viewport_size({'width':390,'height':844}) pg.screenshot(path='/tmp/mobile.png', full_page=True) b.close()"

Then read both PNGs and check them against the rubric. For dev servers, use the `testing-webapps` skill's `with_server.py` helper.

**What to look for that only shows in the render:** text overflowing containers, collapsed layouts at 390px, invisible low-contrast text, elements touching viewport edges, a heading that wraps to one orphaned word, images without dimensions causing layout shift.
python3 -c " from playwright.sync_api import sync_playwright with sync_playwright() as p: b = p.chromium.launch() pg = b.new_page(viewport={'width':1440,'height':900}) pg.goto('file:///abs/path/index.html'); pg.wait_for_timeout(600) pg.screenshot(path='/tmp/desktop.png', full_page=True) pg.set_viewport_size({'width':390,'height':844}) pg.screenshot(path='/tmp/mobile.png', full_page=True) b.close()"

然后查看两张PNG图片,对照评分标准检查。对于开发服务器,使用`testing-webapps`技能中的`with_server.py`工具。

**仅在渲染后才能发现的问题:** 文本溢出容器、390px宽度下布局塌陷、低对比度文本不可见、元素触碰视口边缘、标题换行后只剩一个孤立单词、未设置尺寸的图片导致布局偏移。

Self-Review

自我审查

Score each line honestly. Any
no
means fix it, not explain it.
  • Every spacing value in the CSS is a token from the block
  • The type scale is one ratio; no size was chosen by eye
  • Exactly one accent color, under 10% of visible surface
  • One radius value throughout
  • A stranger could name the aesthetic direction from a screenshot
  • The
    Memorable
    line from the brief is actually present in the build
  • The
    Restraint
    line was actually respected
  • Nothing from the Anti-Slop table appears
  • All five states exist on every interactive element
  • Focus is visible and never suppressed
  • Verified at 1440px and 390px by looking at screenshots
  • Body text passes 4.5:1; large text passes 3:1
诚实地为每一项打分。任何“否”都意味着需要修正,而不是找借口。
  • CSS中的所有间距值都来自令牌块
  • 字体比例使用同一个比值;没有凭视觉选择的字号
  • 仅使用一种强调色,占可见区域比例低于10%
  • 整页使用统一的圆角值
  • 陌生人能从截图中说出美学方向的名称
  • brief中的
    Memorable
    条目确实在最终实现中存在
  • brief中的
    Restraint
    条目确实被遵守
  • 没有出现“避免AI通用化陷阱”中的任何情况
  • 每个交互元素都包含五种状态
  • 焦点可见且从未被隐藏
  • 通过查看截图在1440px和390px尺寸下进行了验证
  • 正文文本对比度达到4.5:1;大文本对比度达到3:1

Reference Files

参考文件

Load these when you reach the relevant decision — not upfront.
FileLoad when
references/aesthetic-directions.md
Step 1 — choosing and parameterizing a direction
references/typography.md
Choosing faces, building the scale, setting text
references/color-and-theme.md
Building the ramp, dark mode, contrast math
references/layout-and-composition.md
Grids, breakpoints, breaking the grid, z-index
references/motion.md
Any animation beyond a hover color change
在需要做相关决策时再加载这些文件——不要提前加载。
文件加载时机
references/aesthetic-directions.md
步骤1——选择并确定美学方向参数时
references/typography.md
选择字体、构建字体比例、设置文本样式时
references/color-and-theme.md
构建色彩梯度、深色模式、对比度计算时
references/layout-and-composition.md
网格布局、断点、打破网格、z-index设置时
references/motion.md
实现悬停颜色变化之外的任何动画时

Common Mistakes

常见错误

Tokens declared, then ignored. The
:root
block exists but components use
padding: 14px
. Grep your CSS for raw px values before finishing.
Distinctive typeface, default everything else. Swapping Inter for a display face while keeping the card grid, the uniform radius, and the gray body text produces generic output in a costume.
Maximalism as an excuse for incoherence. Maximalist directions still need one type scale, one space scale, one palette. More elements, same system.
The brief describes a design the build does not deliver. Re-read the brief at Step 7. If
Restraint: no shadows
and the build has shadows, the build is wrong.
Complexity mismatched to the vision. A refined-minimal direction implemented with elaborate scroll animations reads as confused. Match implementation effort to the stated tone.
令牌已声明但未被使用。
:root
块已定义,但组件中使用了
padding: 14px
这类值。完成前用Grep检查CSS中的原始px值。
使用独特字体,但其他部分仍用默认值。 将Inter换成独特标题字体,但保留卡片网格、统一圆角和灰色正文,只会得到“穿了外套的通用界面”。
用极繁主义作为杂乱的借口。 极繁主义方向仍需要一套统一的字体比例、间距比例和配色方案。元素可以更多,但体系要统一。
brief描述的设计与最终实现不符。 在步骤7重新阅读brief。如果
Restraint: no shadows
但实现中添加了阴影,那实现是错误的。
复杂度与设计愿景不匹配。 以极简精致为方向,却实现了复杂的滚动动画,会显得混乱。实现的复杂度要与声明的风格匹配。