better-typography
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGreat typography
优秀的Web排版
Good typography is mostly restraint. A sensible scale, comfortable spacing and enough contrast beat any clever effect. A label, a table cell, a marketing headline and an article paragraph should not share one set of rules. Apply these principles when building or reviewing anything with text in it.
Match the project's styling system. Before suggesting or writing any fix, check how the codebase styles things and express every change in that system: Tailwind utilities in a Tailwind project, plain declarations in CSS, CSS Modules, styled-components or StyleX. The cheat sheet maps each declaration to its Tailwind equivalent. Never introduce a second styling approach just to apply a typography fix.
优秀的排版大多源于克制。合理的字号层级、舒适的间距与足够的对比度,胜过任何花哨的效果。标签、表格单元格、营销标题与文章段落不应共用一套样式规则。在构建或审查任何含文本的内容时,都应遵循这些原则。
匹配项目的样式体系。在提出或编写任何修复方案前,先查看代码库的样式实现方式,并基于该体系表达所有修改:Tailwind项目使用Tailwind工具类,纯CSS项目使用普通声明,CSS Modules、styled-components或StyleX项目同理。速查表将每个声明映射为对应的Tailwind等效写法。绝不要为了修复排版问题而引入第二种样式方案。
Quick Reference
快速参考
| Category | When to use | Reference |
|---|---|---|
| Choosing fonts | Font categories, pairing, formats, typeface anatomy | choosing-fonts.md |
| Variable fonts & OpenType | Axes, weights, tabular numbers, stylistic sets | variable-fonts-and-opentype.md |
| Spacing & sizing | Type scale, line-height, letter-spacing, text trimming | spacing-and-sizing.md |
| Wrapping & punctuation | Measure, wrapping, truncation, smart punctuation, RTL | wrapping-and-punctuation.md |
| Details & accessibility | Underlines, selection, forms, decorative text, contrast | details-and-accessibility.md |
| CSS cheat sheet | Quick lookup of every property covered, with Tailwind equivalents | css-cheat-sheet.md |
| 分类 | 使用场景 | 参考文档 |
|---|---|---|
| 字体选择 | 字体分类、搭配、格式、字体结构 | choosing-fonts.md |
| 可变字体与OpenType | 轴参数、字重、表格数字、样式集 | variable-fonts-and-opentype.md |
| 间距与尺寸 | 字号比例、行高、字间距、文本裁剪 | spacing-and-sizing.md |
| 换行与标点 | 行宽、换行、截断、智能标点、RTL布局 | wrapping-and-punctuation.md |
| 细节与可访问性 | 下划线、选中文本、表单、装饰文本、对比度 | details-and-accessibility.md |
| CSS速查表 | 快速查找所有涉及的属性及对应的Tailwind等效写法 | css-cheat-sheet.md |
Core Principles
核心原则
1. Serve the Right Format
1. 使用正确的字体格式
Use (Brotli compression, broadly supported) on the web. is a fallback only for very old browsers; and are raw desktop formats with no web compression. How the files are loaded is the project's own concern, this skill does not prescribe it.
.woff2.woff.ttf.otfWeb端优先使用(采用Brotli压缩,支持范围广泛)。仅作为老旧浏览器的降级方案;和是未经过Web压缩的原生桌面格式。字体文件的加载方式由项目自行决定,本指南不做强制规定。
.woff2.woff.ttf.otf2. Properties Over Raw Tags
2. 优先使用标准CSS属性而非原始标签
When a CSS property exists, use it. instead of , instead of , instead of . Properties keep working when a non-variable fallback renders. Reserve the raw-tag properties for custom axes () and niche features () that have no property of their own.
font-weight: 650font-variation-settings: "wght" 650font-optical-sizing: auto"opsz"font-variant-numeric: tabular-numsfont-feature-settings: "tnum" 1"GRAD" 80"ss01" 1当存在对应的CSS属性时,优先使用属性而非原始标签。例如用替代,用替代,用替代。当使用非可变字体作为降级方案时,标准属性仍能正常生效。仅在处理无对应属性的自定义轴(如)和小众特性(如)时,才使用原始标签属性。
font-weight: 650font-variation-settings: "wght" 650font-optical-sizing: auto"opsz"font-variant-numeric: tabular-numsfont-feature-settings: "tnum" 1"GRAD" 80"ss01" 13. No Fake Weights
3. 禁用伪字重/样式
When a weight or style is not loaded, the browser synthesizes it. That is a safety mechanism, not a feature. Set so missing files fail visibly instead of rendering a faked bold or italic.
font-synthesis: none当未加载对应字重或样式的字体文件时,浏览器会自动合成。这是一种安全机制,而非特性。设置,让缺失文件的情况直观显示,而非渲染出伪造的粗体或斜体。
font-synthesis: none4. Fewer Fonts, Sizes and Weights
4. 减少字体、字号与字重的数量
Rarely use more than three fonts. Weight and size define hierarchy, but overusing them hurts readability quickly. Pair for contrast, not similarity: a serif headline with a sans body reads as deliberate, two near-identical sans-serifs read as a mistake.
字体数量尽量不超过3种。字重和字号用于构建层级,但过度使用会迅速损害可读性。选择对比鲜明的字体搭配:衬线标题搭配无衬线正文会显得设计严谨,而两款近乎相同的无衬线字体搭配则会显得像是失误。
5. Use a Type Scale with Semantic Names
5. 使用带语义化名称的字号比例体系
Define a small set of sizes and deviate from it as little as possible. Hard-coded sizes without a system break down at scale. For solo projects, default names like work fine as long as the usage rules are clear. On a team, name sizes by use (), not by size, so the rules stay consistent.
text-smtext-body-sm定义少量固定字号,并尽可能减少偏离。无体系的硬编码字号在项目规模化后会崩溃。对于个人项目,这类默认名称只要使用规则清晰即可。对于团队项目,应按用途命名字号(如)而非按尺寸命名,以保持规则的一致性。
text-smtext-body-sm6. Line-Height by Role
6. 根据文本角色设置行高
Headings tighter, around . Body copy to . Prefer unitless values so line-height scales with the font size; fixed values like do not.
1.11.51.624px标题行高应紧凑,约为。正文行高设置为至。优先使用无单位值,这样行高会随字号自动缩放;而这类固定值则不具备缩放性。
1.11.51.624px7. Letter-Spacing by Size
7. 根据字号设置字间距
Large headings often look better with slightly negative letter-spacing. Small uppercase labels need a little positive letter-spacing so letters do not feel crowded. Body copy at reading sizes needs neither.
大标题通常设置轻微负字间距会更美观。小型大写标签需要少量正字间距,避免字符显得拥挤。阅读尺寸的正文则无需调整字间距。
8. Cap the Measure
8. 限制行宽
Long lines make it hard for the eye to find the next line. Cap long-form text around 60–75 characters per line. Any unit works: measures characters directly, and a pixel or rem cap is just as good — at a body size the range lands roughly between and depending on the font, so Tailwind's or fit. What matters is that a cap exists and the resulting line length sits in range.
65ch16px560px680pxmax-w-xlmax-w-2xl过长的行难以让视线找到下一行。长文本的行宽应限制在每行60-75个字符左右。任何单位均可:直接按字符计量,像素或rem限制也同样有效——在16px正文字号下,该范围大致对应560px至680px(取决于字体),因此Tailwind的或较为合适。关键是要有行宽限制,且最终行长落在合理范围内。
65chmax-w-xlmax-w-2xl9. Wrap Deliberately
9. 合理设置文本换行
text-wrap: balancetext-wrap: prettybalanceoverflow-wrap: break-wordwhite-space: nowraptext-wrap: balancetext-wrap: prettybalanceoverflow-wrap: break-wordwhite-space: nowrap10. Tabular Numbers on Changing Values
10. 动态数值使用表格数字
Digits have different widths by default, so timers, counters and prices shift layout as they update. Apply to any value that changes.
font-variant-numeric: tabular-nums默认情况下,数字字符宽度不同,因此计时器、计数器和价格更新时会导致布局偏移。对所有动态变化的数值应用。
font-variant-numeric: tabular-nums11. Truncate Without Losing Content
11. 文本截断但保留完整内容的可访问性
Single line: with and . Multiple lines: . Truncation hides content, so if the missing text matters, keep the full value reachable in a tooltip or expanded view.
text-overflow: ellipsisoverflow: hiddenwhite-space: nowrapline-clamp单行截断:配合和。多行截断:使用。截断会隐藏内容,因此如果缺失的文本很重要,需通过 tooltip 或展开视图让用户查看完整内容。
text-overflow: ellipsisoverflow: hiddenwhite-space: nowrapline-clamp12. Write Copy Naturally, Style with CSS
12. 按自然格式编写文案,用CSS控制样式
Store text in natural case and control presentation with , so redesigns never require rewriting copy. Use smart punctuation: curly quotes in prose (straight quotes in code), an en dash for ranges like , an em dash to set off a thought, the single ellipsis character, to keep values like together and to control where long words may break.
text-transform2010–2020 16 px­文案按自然大小写存储,通过控制展示样式,这样重新设计时无需修改文案内容。使用智能标点:散文中使用弯引号(代码中使用直引号),范围值如使用短破折号,插入语使用长破折号,使用单个省略号字符,用让这类值不换行,用控制长单词的换行位置。
text-transform2010–2020 16 px­13. Underlines from the Font
13. 使用字体自带的下划线样式
Default underlines sit wherever the browser decides. Pull position and thickness from the font's own metrics with and , or tune manually with , and . draws the line dotted, dashed or wavy; a dotted underline is a common hint that a word carries extra information, like an abbreviation or a defined term. Unless the only thing animating is a color change, build the underline as a separate element instead of using — color is the only part of a real underline that animates reliably.
text-underline-position: from-fonttext-decoration-thickness: from-fonttext-decoration-thicknesstext-underline-offsettext-decoration-skip-inktext-decoration-styletext-decoration默认下划线的位置由浏览器决定。使用和从字体自身 metrics 中获取位置和粗细,或通过、和手动调整。可将下划线设置为点状、虚线或波浪线;点状下划线通常用于提示单词带有额外信息,如缩写或定义术语。除非仅动画颜色变化,否则应将下划线作为独立元素构建,而非使用——原生下划线中只有颜色能可靠地实现动画效果。
text-underline-position: from-fonttext-decoration-thickness: from-fonttext-decoration-thicknesstext-underline-offsettext-decoration-skip-inktext-decoration-styletext-decoration14. Inputs at 16px on Mobile
14. 移动端输入框字号不小于16px
iOS Safari zooms the whole page when an input's text is smaller than . Keep input text at on mobile viewports (). Avoid the viewport meta: Safari ignores it for pinch zoom, but every other browser honors it and blocks zooming, which fails WCAG.
16px16pxtext-base sm:text-smmaximum-scale=1当输入框文本小于16px时,iOS Safari会缩放整个页面。移动端视口中输入框文本应保持16px(可使用)。避免使用视口元标签:Safari会忽略它以支持 pinch 缩放,但其他浏览器会生效并阻止缩放,这不符合WCAG标准。
text-base sm:text-smmaximum-scale=115. Size and Contrast Floors
15. 字号与对比度下限
Body text (the web default and the right reading size). UI text can go smaller: for inputs and menus (inputs still need on mobile, see principle 14), for captions, rarely below . WCAG AA: contrast for regular text, for large text (roughly and up).
16px14px16px13px12px4.5:13:124px正文字号为16px(Web默认值,也是合适的阅读尺寸)。UI文本可更小:输入框和菜单使用14px(移动端输入框仍需16px,见原则14),说明文字使用13px,尽量不低于12px。WCAG AA标准:普通文本对比度为4.5:1,大文本(约24px及以上)对比度为3:1。
16. Font Smoothing on the Root
16. 根元素设置字体平滑
On macOS text renders heavier than intended. Apply and (both covered by Tailwind's ) once on the root layout so they cover all text.
-webkit-font-smoothing: antialiased-moz-osx-font-smoothing: grayscaleantialiasedmacOS上文本渲染会比预期更厚重。在根布局上应用和(两者均被Tailwind的类覆盖),确保所有文本都应用该效果。
-webkit-font-smoothing: antialiased-moz-osx-font-smoothing: grayscaleantialiased17. Logical Properties for Direction
17. 使用逻辑属性适配文本方向
To support right-to-left content, use direction-agnostic properties: instead of , instead of . Set so browsers pick the right quotes and hyphenation, and where needed.
margin-inline-startmargin-lefttext-align: startleftlangdir="rtl"为了支持从右到左(RTL)的内容,使用与方向无关的属性:用替代,用替代。设置属性让浏览器选择正确的引号和连字符,在需要的位置设置。
margin-inline-startmargin-lefttext-align: startleftlangdir="rtl"18. Style the Selection, Disable It Where It Distracts
18. 自定义选中文本样式,在干扰场景下禁用选择
::selectionuser-select: nonecmd+A::selectionuser-select: nonecmd+AReview Output Format
审查输出格式
Always present changes as a markdown table with Before and After columns. Include every change you made, not just a subset. Never list findings as separate "Before:" / "After:" lines outside of a table. Group changes by principle using a heading above each table, and keep each row focused on a single diff. Write every After snippet in the styling system the project already uses.
始终将修改内容以Markdown表格形式呈现,包含修改前和修改后列。列出所有修改内容,而非仅子集。绝不要将结果以表格外的单独“修改前:”/“修改后:”行列出。按原则分组,在每个表格上方添加标题,每行聚焦单个差异。所有修改后的代码片段都应使用项目已有的样式体系编写。
Example
示例
Tabular numbers
表格数字
| Before | After |
|---|---|
| |
| |
| 修改前 | 修改后 |
|---|---|
实时价格的 | |
| |
Line-height and measure
行高与行宽
| Before | After |
|---|---|
| |
| Full-width article column | |
Rows should cite the specific file and property when it is not obvious from the snippet. If a principle was reviewed but nothing needed to change, omit that table entirely.
| 修改前 | 修改后 |
|---|---|
正文段落使用 | |
| 全宽文章列 | |
当代码片段无法明确显示时,应注明具体文件和属性。如果某条原则已审查但无需修改,则省略对应的表格。
Common Mistakes
常见错误
| Mistake | Fix |
|---|---|
| Convert to |
| |
| |
| Browser-faked bold or italic | Load the file, set |
| Hard-coded one-off font sizes | Use the type scale |
| Unitless value ( |
| Full-width paragraphs | Cap around 60–75 characters per line |
| Orphan on the last line of a paragraph | |
| Lopsided two-line heading | |
| Numbers cause layout shift | |
| Truncated text with no way to read it | Tooltip or expanded view for the full value |
| Natural case + |
| Justified text in an interface | |
| Underline cuts through descenders | |
Inputs below | |
| |
| Selectable button labels in native-feel UI | |
| Extra-info hint with no visual cue | Dotted underline via |
| Tailwind classes dropped into a CSS-in-JS codebase (or the reverse) | Express the fix in the styling system the project already uses |
| 错误 | 修复方案 |
|---|---|
Web端使用 | 转换为 |
使用 | 使用 |
使用 | 使用 |
| 浏览器合成粗体或斜体 | 加载对应字体文件,设置 |
| 硬编码一次性字号 | 使用字号比例体系 |
可缩放文本使用 | 使用无单位值(如 |
| 正文全宽显示 | 限制每行约60–75个字符 |
| 段落最后一行仅单个单词 | 使用 |
| 两行标题排版失衡 | 使用 |
| 数字导致布局偏移 | 使用 |
| 截断文本无法查看完整内容 | 为完整内容添加tooltip或展开视图 |
文案中直接输入 | 自然大小写 + |
| 界面中使用两端对齐文本 | 使用 |
| 下划线穿过下行字母 | 使用 |
| iOS上输入框字号小于16px导致缩放 | 使用 |
支持RTL的UI中使用 | 使用 |
| 类原生UI中按钮标签可选中 | 使用 |
| 额外信息提示无视觉标识 | 通过 |
| 在CSS-in-JS代码库中使用Tailwind类(反之亦然) | 使用项目已有的样式体系实现修复 |
Review Checklist
审查清单
- Web fonts are
.woff2 - /
font-weightused instead of raw axis and feature tagsfont-variant-* - set; no faked weights or styles
font-synthesis: none - Sizes come from the type scale, no one-off values
- Headings ~line-height, body
1.1–1.5, unitless1.6 - Large headings have slightly negative tracking, small uppercase labels positive
- Long-form text capped around 60–75 characters per line
- Headings use , body uses
text-wrap: balancetext-wrap: pretty - Changing numbers use
tabular-nums - Truncated content is reachable in full somewhere
- Copy stored in natural case, presentation via
text-transform - Underlines use or tuned thickness, offset and skip-ink
from-font - Inputs are + on mobile viewports
16px - Text sizes and contrast meet the floors (body,
16px/4.5:1)3:1 - applied once on the root layout
antialiased - Directional properties are logical (,
inline-start)start - Any styled stays legible
::selection
- Web字体使用格式
.woff2 - 使用/
font-weight而非原始轴参数和特性标签font-variant-* - 设置;无伪造字重或样式
font-synthesis: none - 字号来自字号比例体系,无一次性值
- 标题行高约1.1,正文行高1.5–1.6,使用无单位值
- 大标题设置轻微负字间距,小型大写标签设置正字间距
- 长文本行宽限制在每行约60–75个字符
- 标题使用,正文使用
text-wrap: balancetext-wrap: pretty - 动态变化的数字使用
tabular-nums - 截断内容可通过其他方式查看完整版本
- 文案按自然大小写存储,通过控制展示样式
text-transform - 下划线使用或手动调整粗细、偏移量和避墨效果
from-font - 移动端视口中输入框字号不小于16px
- 文本尺寸和对比度符合下限要求(正文字号16px,对比度4.5:1/3:1)
- 根布局应用
antialiased - 使用逻辑属性(、
inline-start)适配文本方向start - 自定义的样式保持清晰可读
::selection