better-typography

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Great 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

快速参考

CategoryWhen to useReference
Choosing fontsFont categories, pairing, formats, typeface anatomychoosing-fonts.md
Variable fonts & OpenTypeAxes, weights, tabular numbers, stylistic setsvariable-fonts-and-opentype.md
Spacing & sizingType scale, line-height, letter-spacing, text trimmingspacing-and-sizing.md
Wrapping & punctuationMeasure, wrapping, truncation, smart punctuation, RTLwrapping-and-punctuation.md
Details & accessibilityUnderlines, selection, forms, decorative text, contrastdetails-and-accessibility.md
CSS cheat sheetQuick lookup of every property covered, with Tailwind equivalentscss-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
.woff2
(Brotli compression, broadly supported) on the web.
.woff
is a fallback only for very old browsers;
.ttf
and
.otf
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.
Web端优先使用
.woff2
(采用Brotli压缩,支持范围广泛)。
.woff
仅作为老旧浏览器的降级方案;
.ttf
.otf
是未经过Web压缩的原生桌面格式。字体文件的加载方式由项目自行决定,本指南不做强制规定。

2. Properties Over Raw Tags

2. 优先使用标准CSS属性而非原始标签

When a CSS property exists, use it.
font-weight: 650
instead of
font-variation-settings: "wght" 650
,
font-optical-sizing: auto
instead of
"opsz"
,
font-variant-numeric: tabular-nums
instead of
font-feature-settings: "tnum" 1
. Properties keep working when a non-variable fallback renders. Reserve the raw-tag properties for custom axes (
"GRAD" 80
) and niche features (
"ss01" 1
) that have no property of their own.
当存在对应的CSS属性时,优先使用属性而非原始标签。例如用
font-weight: 650
替代
font-variation-settings: "wght" 650
,用
font-optical-sizing: auto
替代
"opsz"
,用
font-variant-numeric: tabular-nums
替代
font-feature-settings: "tnum" 1
。当使用非可变字体作为降级方案时,标准属性仍能正常生效。仅在处理无对应属性的自定义轴(如
"GRAD" 80
)和小众特性(如
"ss01" 1
)时,才使用原始标签属性。

3. 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
font-synthesis: none
so missing files fail visibly instead of rendering a faked bold or italic.
当未加载对应字重或样式的字体文件时,浏览器会自动合成。这是一种安全机制,而非特性。设置
font-synthesis: none
,让缺失文件的情况直观显示,而非渲染出伪造的粗体或斜体。

4. 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
text-sm
work fine as long as the usage rules are clear. On a team, name sizes by use (
text-body-sm
), not by size, so the rules stay consistent.
定义少量固定字号,并尽可能减少偏离。无体系的硬编码字号在项目规模化后会崩溃。对于个人项目,
text-sm
这类默认名称只要使用规则清晰即可。对于团队项目,应按用途命名字号(如
text-body-sm
)而非按尺寸命名,以保持规则的一致性。

6. Line-Height by Role

6. 根据文本角色设置行高

Headings tighter, around
1.1
. Body copy
1.5
to
1.6
. Prefer unitless values so line-height scales with the font size; fixed values like
24px
do not.
标题行高应紧凑,约为
1.1
。正文行高设置为
1.5
1.6
。优先使用无单位值,这样行高会随字号自动缩放;而
24px
这类固定值则不具备缩放性。

7. 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:
65ch
measures characters directly, and a pixel or rem cap is just as good — at a
16px
body size the range lands roughly between
560px
and
680px
depending on the font, so Tailwind's
max-w-xl
or
max-w-2xl
fit. What matters is that a cap exists and the resulting line length sits in range.
过长的行难以让视线找到下一行。长文本的行宽应限制在每行60-75个字符左右。任何单位均可:
65ch
直接按字符计量,像素或rem限制也同样有效——在16px正文字号下,该范围大致对应560px至680px(取决于字体),因此Tailwind的
max-w-xl
max-w-2xl
较为合适。关键是要有行宽限制,且最终行长落在合理范围内。

9. Wrap Deliberately

9. 合理设置文本换行

text-wrap: balance
distributes text evenly across lines: use it on headings.
text-wrap: pretty
avoids leaving a single short word on the final line: use it on descriptions. Skip both in long-form text: browsers ignore
balance
past a few lines anyway, and evening out a whole paragraph wastes space and makes it harder to read.
overflow-wrap: break-word
where long words, links or IDs could escape the container.
white-space: nowrap
on labels and badges where a line break looks broken.
text-wrap: balance
会让文本在多行间均匀分布:适用于标题。
text-wrap: pretty
可避免最后一行仅出现单个短词:适用于描述文本。长文本则无需使用这两个属性:浏览器对超过几行的文本会忽略
balance
,且为了均匀分布整个段落会浪费空间并降低可读性。在长单词、链接或ID可能超出容器的位置,设置
overflow-wrap: break-word
。标签和徽章等不应换行的元素,设置
white-space: nowrap

10. Tabular Numbers on Changing Values

10. 动态数值使用表格数字

Digits have different widths by default, so timers, counters and prices shift layout as they update. Apply
font-variant-numeric: tabular-nums
to any value that changes.
默认情况下,数字字符宽度不同,因此计时器、计数器和价格更新时会导致布局偏移。对所有动态变化的数值应用
font-variant-numeric: tabular-nums

11. Truncate Without Losing Content

11. 文本截断但保留完整内容的可访问性

Single line:
text-overflow: ellipsis
with
overflow: hidden
and
white-space: nowrap
. Multiple lines:
line-clamp
. Truncation hides content, so if the missing text matters, keep the full value reachable in a tooltip or expanded view.
单行截断:
text-overflow: ellipsis
配合
overflow: hidden
white-space: nowrap
。多行截断:使用
line-clamp
。截断会隐藏内容,因此如果缺失的文本很重要,需通过 tooltip 或展开视图让用户查看完整内容。

12. Write Copy Naturally, Style with CSS

12. 按自然格式编写文案,用CSS控制样式

Store text in natural case and control presentation with
text-transform
, so redesigns never require rewriting copy. Use smart punctuation: curly quotes in prose (straight quotes in code), an en dash for ranges like
2010–2020
, an em dash to set off a thought, the single ellipsis character,
 
to keep values like
16 px
together and
­
to control where long words may break.
文案按自然大小写存储,通过
text-transform
控制展示样式,这样重新设计时无需修改文案内容。使用智能标点:散文中使用弯引号(代码中使用直引号),范围值如
2010–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
text-underline-position: from-font
and
text-decoration-thickness: from-font
, or tune manually with
text-decoration-thickness
,
text-underline-offset
and
text-decoration-skip-ink
.
text-decoration-style
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
text-decoration
— color is the only part of a real underline that animates reliably.
默认下划线的位置由浏览器决定。使用
text-underline-position: from-font
text-decoration-thickness: from-font
从字体自身 metrics 中获取位置和粗细,或通过
text-decoration-thickness
text-underline-offset
text-decoration-skip-ink
手动调整。
text-decoration-style
可将下划线设置为点状、虚线或波浪线;点状下划线通常用于提示单词带有额外信息,如缩写或定义术语。除非仅动画颜色变化,否则应将下划线作为独立元素构建,而非使用
text-decoration
——原生下划线中只有颜色能可靠地实现动画效果。

14. Inputs at 16px on Mobile

14. 移动端输入框字号不小于16px

iOS Safari zooms the whole page when an input's text is smaller than
16px
. Keep input text at
16px
on mobile viewports (
text-base sm:text-sm
). Avoid the
maximum-scale=1
viewport meta: Safari ignores it for pinch zoom, but every other browser honors it and blocks zooming, which fails WCAG.
当输入框文本小于16px时,iOS Safari会缩放整个页面。移动端视口中输入框文本应保持16px(可使用
text-base sm:text-sm
)。避免使用
maximum-scale=1
视口元标签:Safari会忽略它以支持 pinch 缩放,但其他浏览器会生效并阻止缩放,这不符合WCAG标准。

15. Size and Contrast Floors

15. 字号与对比度下限

Body text
16px
(the web default and the right reading size). UI text can go smaller:
14px
for inputs and menus (inputs still need
16px
on mobile, see principle 14),
13px
for captions, rarely below
12px
. WCAG AA:
4.5:1
contrast for regular text,
3:1
for large text (roughly
24px
and up).
正文字号为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
-webkit-font-smoothing: antialiased
and
-moz-osx-font-smoothing: grayscale
(both covered by Tailwind's
antialiased
) once on the root layout so they cover all text.
macOS上文本渲染会比预期更厚重。在根布局上应用
-webkit-font-smoothing: antialiased
-moz-osx-font-smoothing: grayscale
(两者均被Tailwind的
antialiased
类覆盖),确保所有文本都应用该效果。

17. Logical Properties for Direction

17. 使用逻辑属性适配文本方向

To support right-to-left content, use direction-agnostic properties:
margin-inline-start
instead of
margin-left
,
text-align: start
instead of
left
. Set
lang
so browsers pick the right quotes and hyphenation, and
dir="rtl"
where needed.
为了支持从右到左(RTL)的内容,使用与方向无关的属性:用
margin-inline-start
替代
margin-left
,用
text-align: start
替代
left
。设置
lang
属性让浏览器选择正确的引号和连字符,在需要的位置设置
dir="rtl"

18. Style the Selection, Disable It Where It Distracts

18. 自定义选中文本样式,在干扰场景下禁用选择

::selection
is a subtle way to embed brand in the reading experience; keep the combination legible. Use
user-select: none
on button labels where copying is unlikely and selection feels distracting, and make sure
cmd+A
only grabs text the user expects to copy. In cross-platform apps that feel closer to native, disable selection for the interface and keep it only on content worth copying.
::selection
是在阅读体验中融入品牌元素的微妙方式;需确保样式组合清晰可读。在不太可能被复制且选中文本会干扰体验的按钮标签上,使用
user-select: none
,并确保
cmd+A
仅选中用户预期复制的文本。在接近原生体验的跨平台应用中,禁用界面元素的文本选择,仅保留值得复制的内容的选择功能。

Review 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

表格数字

BeforeAfter
<span>{price}</span>
on live price
<span className="tabular-nums">{price}</span>
font-feature-settings: "tnum" 1
font-variant-numeric: tabular-nums
修改前修改后
实时价格的
<span>{price}</span>
<span className="tabular-nums">{price}</span>
font-feature-settings: "tnum" 1
font-variant-numeric: tabular-nums

Line-height and measure

行高与行宽

BeforeAfter
leading-none
on body paragraph
leading-normal
(body needs
1.5
1.6
)
Full-width article column
max-w-2xl
(~65 characters per line at
16px
)
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.
修改前修改后
正文段落使用
leading-none
leading-normal
(正文行高需1.5–1.6)
全宽文章列
max-w-2xl
(16px字号下约每行65个字符)
当代码片段无法明确显示时,应注明具体文件和属性。如果某条原则已审查但无需修改,则省略对应的表格。

Common Mistakes

常见错误

MistakeFix
.ttf
/
.otf
served on the web
Convert to
.woff2
font-variation-settings: "wght"
for weight
font-weight
(works with non-variable fallbacks)
font-feature-settings: "tnum" 1
font-variant-numeric: tabular-nums
Browser-faked bold or italicLoad the file, set
font-synthesis: none
Hard-coded one-off font sizesUse the type scale
line-height: 24px
on scalable text
Unitless value (
1.5
)
Full-width paragraphsCap around 60–75 characters per line
Orphan on the last line of a paragraph
text-wrap: pretty
Lopsided two-line heading
text-wrap: balance
Numbers cause layout shift
tabular-nums
Truncated text with no way to read itTooltip or expanded view for the full value
UPPERCASE
typed into copy
Natural case +
text-transform
Justified text in an interface
text-align: start
; reserve justify for specific editorial layouts
Underline cuts through descenders
text-decoration-skip-ink: auto
,
from-font
metrics
Inputs below
16px
zoom on iOS
text-base sm:text-sm
margin-left
in RTL-capable UI
margin-inline-start
Selectable button labels in native-feel UI
user-select: none
, keep selection on real content
Extra-info hint with no visual cueDotted underline via
text-decoration-style: dotted
Tailwind classes dropped into a CSS-in-JS codebase (or the reverse)Express the fix in the styling system the project already uses
错误修复方案
Web端使用
.ttf
/
.otf
字体
转换为
.woff2
格式
使用
font-variation-settings: "wght"
设置字重
使用
font-weight
(兼容非可变字体降级方案)
使用
font-feature-settings: "tnum" 1
使用
font-variant-numeric: tabular-nums
浏览器合成粗体或斜体加载对应字体文件,设置
font-synthesis: none
硬编码一次性字号使用字号比例体系
可缩放文本使用
line-height: 24px
使用无单位值(如
1.5
正文全宽显示限制每行约60–75个字符
段落最后一行仅单个单词使用
text-wrap: pretty
两行标题排版失衡使用
text-wrap: balance
数字导致布局偏移使用
tabular-nums
截断文本无法查看完整内容为完整内容添加tooltip或展开视图
文案中直接输入
UPPERCASE
自然大小写 +
text-transform
控制样式
界面中使用两端对齐文本使用
text-align: start
;两端对齐仅用于特定编辑排版
下划线穿过下行字母使用
text-decoration-skip-ink: auto
from-font
metrics
iOS上输入框字号小于16px导致缩放使用
text-base sm:text-sm
支持RTL的UI中使用
margin-left
使用
margin-inline-start
类原生UI中按钮标签可选中使用
user-select: none
,仅保留真实内容的选择功能
额外信息提示无视觉标识通过
text-decoration-style: dotted
添加点状下划线
在CSS-in-JS代码库中使用Tailwind类(反之亦然)使用项目已有的样式体系实现修复

Review Checklist

审查清单

  • Web fonts are
    .woff2
  • font-weight
    /
    font-variant-*
    used instead of raw axis and feature tags
  • font-synthesis: none
    set; no faked weights or styles
  • Sizes come from the type scale, no one-off values
  • Headings ~
    1.1
    line-height, body
    1.5
    1.6
    , unitless
  • Large headings have slightly negative tracking, small uppercase labels positive
  • Long-form text capped around 60–75 characters per line
  • Headings use
    text-wrap: balance
    , body uses
    text-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
    from-font
    or tuned thickness, offset and skip-ink
  • Inputs are
    16px
    + on mobile viewports
  • Text sizes and contrast meet the floors (
    16px
    body,
    4.5:1
    /
    3:1
    )
  • antialiased
    applied once on the root layout
  • Directional properties are logical (
    inline-start
    ,
    start
    )
  • Any styled
    ::selection
    stays legible
  • Web字体使用
    .woff2
    格式
  • 使用
    font-weight
    /
    font-variant-*
    而非原始轴参数和特性标签
  • 设置
    font-synthesis: none
    ;无伪造字重或样式
  • 字号来自字号比例体系,无一次性值
  • 标题行高约1.1,正文行高1.5–1.6,使用无单位值
  • 大标题设置轻微负字间距,小型大写标签设置正字间距
  • 长文本行宽限制在每行约60–75个字符
  • 标题使用
    text-wrap: balance
    ,正文使用
    text-wrap: pretty
  • 动态变化的数字使用
    tabular-nums
  • 截断内容可通过其他方式查看完整版本
  • 文案按自然大小写存储,通过
    text-transform
    控制展示样式
  • 下划线使用
    from-font
    或手动调整粗细、偏移量和避墨效果
  • 移动端视口中输入框字号不小于16px
  • 文本尺寸和对比度符合下限要求(正文字号16px,对比度4.5:1/3:1)
  • 根布局应用
    antialiased
  • 使用逻辑属性(
    inline-start
    start
    )适配文本方向
  • 自定义的
    ::selection
    样式保持清晰可读