
做前端这些年我发现被问得最多的往往不是那些看起来很酷的动画效果而是最基础的文本排版问题字体为什么总跟设计稿对不上、图片和文字为什么老对不齐、一句话在容器里到底怎么摆才体面。CSS 文本样式和文本布局看着简单真往深了挖处处都是细节。今天我把这套东西从头到尾梳理一遍顺便把几个高频小知识一起讲了适合刚接触 HTMLCSS 的新手也适合写了两三年 CSS 但基本靠试错式开发的朋友看完可以直接对照着自己项目改。1. 文本样式地基字体、字号、粗细与行高的正确打开方式1.1 font-family 的优先级机制与中文字体陷阱很多人写 font-family 就是随便抄一行比如body { font-family: Arial, Helvetica, sans-serif; }这段代码在纯英文页面上没问题但一旦遇到中文内容Arial 和 Helvetica 都没有中文字形浏览器就会一路往下找直到遇到系统默认的中文字体。这就引出了 font-family 的第一个核心机制它不是指定一个字体而是给浏览器一个字体候选列表按顺序逐个尝试直到找到能显示当前字符的字体为止。所以中文字体栈的正确写法应该把中文字体放在英文和数字字体之后body { font-family: Helvetica Neue, Arial, PingFang SC, Microsoft YaHei, sans-serif; }这样英文和数字优先用 Helvetica Neue 这类更精致的西文字体中文再回落到苹方或微软雅黑。如果你直接把 PingFang SC 写在最前面英文字母也会用苹方渲染有些场景下数字看着就没那么精神。中文字体这里有几个经典坑要提醒一下宋体是 SimSun黑体是 SimHei微软雅黑是 Microsoft YaHei直接写中文别名在某些老浏览器里能识别但不规范我建议一律用标准英文名。按系统规范写法可以带引号也可以不带但包含空格的字体名必须加引号。不要在一个 font-family 里堆七八个中文字体没有意义。因为中文字体体积大系统一般只有一两款写再多也只是在几个差不多的字体之间反复横跳。如果项目里有特殊设计字体比如标题用了某款仅用于展示的艺术字体不要直接写进 body 的 font-family应该单独给标题元素设置并且用 font-face 加载 Web 字体font-face { font-family: MyTitleFont; src: url(./fonts/MyTitleFont.woff2) format(woff2); font-weight: 400; font-display: swap; }这里font-display: swap的意思是字体文件还没加载完时先用系统字体渲染加载完再替换。代价是会出现一次字体闪烁FOUT但总比页面白屏等字体强。标题这种几个字的场景闪烁影响不大正文千万不要用这种字体一大段文字加载前后跳变阅读体验非常差。1.2 font-size 与行高正文排版的隐形骨架font-size 的选择是排版的第一步。中文正文在普通显示器上14px 是舒适下限16px 是最稳的默认值。设计稿里常见的 12px通常只用于辅助说明文字、标签类信息不能大段使用否则阅读起来非常累。关于字体大小单位 px、em、rem 的取舍第 4 节我会展开聊这里先说一个更重要的东西行高line-height才是决定正文可读性的隐形骨架。先看行高的单位陷阱。line-height 有四种写法写法计算方式继承行为踩坑点line-height: normal由浏览器决定通常 1.1~1.2继承计算值不同浏览器差异大line-height: 24px固定像素继承固定值字号一变行高就失调line-height: 150%相对当前元素字号计算继承计算后的像素值嵌套元素会继承算好的值子元素字号变了也改不动line-height: 1.6相对当前元素字号继承这个倍率最安全推荐我推荐正文一律写成无单位的数字比如body { font-size: 16px; line-height: 1.6; }这样每个子元素都会继承1.6 倍自身字号这个比例无论哪个元素字号多大行高都自动匹配不会出现文字挤在一起或行距过大的问题。中文正文的行高建议在 1.5~1.7 之间低于 1.5 会显得拥挤高于 1.8 又会显得段落松散。行高还有一个经典用途单行文字的垂直居中。给容器设置一个高度再把 line-height 设为相同值文字就会垂直居中.btn { height: 40px; line-height: 40px; text-align: center; }这个技巧我从老项目用到新项目依然好使。但是要注意只有单行文本才能这么干。一旦文字换行变成两行这个方案就直接废掉两行文字会被撑出容器或互相重叠。多行文字的垂直居中老老实实用 flex 或者 padding 来调别硬套 line-height 的居中技巧。1.3 font-weight 数字值、font-style 与 font 简写顺序font-weight 大多数人只写过 normal 和 bold但规范里定义的是 100 到 900 的九档数字400 等于 normal700 等于 bold。为什么建议用数字因为现在可变字体variable fonts越来越普及一个字体文件里可以包含连续的字重范围比如 100~900 都能渲染。写数字比写关键词更精确也更容易记忆。这里有个实际工作中一定会碰到的问题如果你指定font-weight: 600但当前字体文件里没有 600 这一档浏览器会拿最近的档位来渲染。比如思源黑体有 100/200/300/400/500/700/900 档没有 600那么 600 就会落到 700 上你在页面上看到的其实是粗体而不是介于中间的效果。更麻烦的是有些浏览器的合成加粗faux bold会把本来没有粗体的字硬生生拉粗观感很差。所以字体权重要跟字体文件的实际档位对得上。font-style 除了 normal 和 italic还有个容易被忽略的 oblique。两者的区别是italic 通常调用字库里的斜体版本是设计师专门设计的oblique 是浏览器把正体机械地倾斜出来的。中文生态里专门的斜体字库很少所以中文里写 italic 很多时候也是浏览器硬斜的效果一般。日常中文字体的斜体需求我一般建议用transform: skew配合一点点角度比 font-style 的可控性更好。最后是 font 简写。它的完整顺序是font: font-style font-weight font-variant font-size/line-height font-family;实际写的时候style、weight、variant 可以省略但font-size 和 font-family 是必填项缺一个整条规则都无效。例如.title { font: 700 24px/1.4 PingFang SC, Microsoft YaHei, sans-serif; }这里24px/1.4表示字号 24px、行高 1.4。用 font 简写有个隐藏副作用它会把 font-size、line-height、font-family、font-weight、font-style、font-variant、font-stretch 一次性全部重置哪怕你没写的那几项也会被重置成默认值。所以如果你只是想改个字号千万别用 font 简写覆盖已有规则就写font-size: 24px这一条否则其他字体属性会被连坐重置。2. 文本布局核心对齐、缩进、间距与方向控制2.1 text-align 四种取值以及 justify 的两端对齐陷阱text-align 的取值看着就那么几个left、right、center、justify。前三个不用多说重点说 justify两端对齐——这是中文排版里最容易被误解的一个属性。justify 的本意是让每一行的左右两端都对齐做法是拉伸这一行单词或字之间的空白间距。这在英文报纸排版里很常见但放到中文里有个致命问题最后一行如果内容不满不会被拉伸于是你会看到一行被拉得稀稀拉拉下一行却紧密排列段落右侧呈现明显的锯齿状缺口。更糟的是当某一行恰好只有两个词时justify 会把它们之间的距离拉得特别大视觉上像出了排版事故。如果你真的需要两端对齐的效果比如做仿印刷品的段落有两个补充手段p { text-align: justify; text-align-last: left; }text-align-last: left可以让最后一行保持左对齐避免被拉伸。但 Safari 对 text-align-last 的兼容性一直不太理想需要写-webkit-text-align-last: left。另外提醒一下text-align 有个姊妹写法是 start 和 end。这两个值不是简单对应 left/right而是跟文字的书写方向绑定在从左到右的语言里 start 等于 left在从右到左的语言里 start 等于 right。如果你做国际化项目优先用 start/end能省很多方向适配的麻烦。2.2 text-indent 首行缩进与 letter-spacing、word-spacing 的差别中文排版里段落首行缩进两个字是基本规范对应的属性是 text-indentp { text-indent: 2em; }注意这里我用的是em而不是px。em 是相对当前元素字号计算的缩进两个字用 2em 正好。如果你写text-indent: 32px一旦字号调整缩进就跟字号脱节看起来就不像是两个字了。这也是 text-indent 最适合用 em 的场景。text-indent 还能取负值配合 padding 可以实现悬挂缩进.hanging { text-indent: -2em; padding-left: 2em; }这种效果在列表、引用块里很常用第一行文字顶着左边后续行全部缩进两个字的宽度。接着是 letter-spacing 和 word-spacing这俩经常被混为一谈。letter-spacing 是字符间距对中文、英文都生效word-spacing 只作用在单词之间的空格上对中文这种没有空格分隔的语言设置多少都没用。所以中文排版里做字距调整用的永远是 letter-spacing。标题加一点点 letter-spacing 确实能提升精致感比如h2 { letter-spacing: 2px; }但正文不要随便加中文正文加了字距会显得松散而且会增加换行的不确定性。还有一个容易忽略的细节text-indent 缩进的空格也是文字letter-spacing 会把缩进也放大。如果你同时设置了 text-indent 和 letter-spacing会发现首行缩进比预期的多出两个字的间距这就是 letter-spacing 作用于缩进空格导致的遇到这种情况把缩进值减去 letter-spacing 的影响就行。跟间距相关的还有 white-space 系列日常最常用的是white-space: nowrap禁止换行和white-space: pre-wrap保留换行符且允许自动换行。比如做标签、按钮里的文字为了防止内容被压缩换行加个 nowrap 几乎是标配。2.3 文本方向与书写模式writing-mode、direction、unicode-bidi文本方向布局这块大多数前端一辈子可能都用不到几次但文本方向布局确实是搜索热词说明还是有不少人在某些特殊场景下碰到了。最常见的场景是中文竖排。CSS 提供了 writing-mode 属性.vertical-text { writing-mode: vertical-rl; text-orientation: upright; }writing-mode 的三个值分别是horizontal-tb默认从左到右水平书写从上到下换行vertical-rl从右到左垂直书写这是传统中文竖排的方式vertical-lr从左到右垂直书写用 vertical-rl 做出来的竖排文字跟古籍的排版方向一致。配合 text-orientation: upright 可以把英文字母和数字也转正不至于歪着。如果你给一段中文竖排设了 text-align: center它作用的方向会跟着书写模式改变——在 vertical-rl 下text-align 控制的是水平方向上的居中而不是垂直方向千万别按横排的直觉去调。direction 属性更常见于阿拉伯语、希伯来语等从右到左的语言用法是.rtl { direction: rtl; }设置 direction: rtl 后普通的文本流会从右往左排列标点符号的朝向也会变化。unicode-bidi 是配合 direction 控制双向文本嵌套的绝大多数情况下你不需要直接操作它浏览器会按 Unicode 双向算法自动处理。只有在做多语言混排的高级场景才需要手动干预这里不展开。实际工作里竖排文本最常用的场景其实不是整篇文章而是单个标题、印章效果、海报上的辅助文字。这种几个字的场景用 writing-mode 完全够用。如果是一整本竖排书那样的长文建议还是交给排版软件浏览器里做长文竖排的体验依然不算好。3. 图片与文字一行从基线对齐到 Flex 布局的演进3.1 vertical-align 的基线机制与多出 3px之谜图片和文字一行绝对是前端新手提问率最高的问题没有之一。先看一个几乎所有新手都踩过的坑div classcard img srcicon.png alt 这是一行文字 /div.card img { width: 20px; height: 20px; }你会发图片底部和文字之间有一条很难看的空隙怎么也消不掉调 margin 也奇怪。这条空隙的根源是 vertical-align 的默认值 baseline。CSS 的基线机制要这样理解所有行内元素包括图片、行内块元素默认都坐在文本基线上而基线不是文字的底部是字母 x 的底部。基线下方还要给带尾巴的字母如 g、j、p、y留出空间这部分叫下降部。图片坐在基线上它下面自然就空出了一截下降部的高度看起来就是那条神秘的空白。解决思路有三个任选其一/* 思路一让图片不参与基线对齐 */ .card img { display: block; } /* 思路二改变对齐方式 */ .card img { vertical-align: middle; } /* 思路三用 top/bottom 对齐 */ .card img { vertical-align: bottom; }vertical-align 的取值表我整理了一下取值对齐参照典型场景baseline文本基线默认最容易出问题middle父元素 x 字母高度的中点小图标与文字同排top / bottom行盒的顶部 / 底部大头像缩略图sub / super下标 / 上标数学公式、脚注数值如 -5px相对基线偏移微调单像素对齐另外vertical-align 不只是给图片用的它也能用于行内元素、行内块元素以及表格单元格的垂直对齐。td里的内容默认就是 vertical-align: middle这个在 ide 原生表格里很省心。3.2 Flex 与 Grid 下的一行排版标准姿势上面说的 vertical-align 解决的是行内元素共存的老问题。但现在做一行图文排版我更推荐直接用 Flex省心得多.item { display: flex; align-items: center; gap: 8px; }为什么推荐这个方案因为 Flex 根本不走基线那套逻辑align-items: center是让子元素在交叉轴上居中图片和文字天然对齐不存在下降部引发的空隙。gap属性还顺手解决了图文间距的问题不需要再给图片单独加 margin-right。对于更复杂的场景Flex 还能控制文字部分自适应.item { display: flex; align-items: center; gap: 12px; } .item .text { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }这种结构在列表页、评论区、消息中心里极其常见左边头像固定宽度右边文字区域可伸缩超出部分显示省略号。如果不用 Flex想做到文字占满剩余宽度并截断得写一堆 float 和 overflow 的 hack现在一行 flex: 1 搞定。还有个小技巧Flex 里的子项如果要做 1px 级别的微调别去改 margin 或 padding 破坏整体布局。用transform: translate做微偏移比如transform: translateY(2px)它不影响文档流里的其他元素也不会引发奇怪的间距 bug。这属于css 平移的高频用法之一在很多需要像素级对齐的场景下很管用。3.3 容器内文本位置调整的完整思路接着讲一个搜索频率很高的问题怎么调整 css 容器里的文本位置。这个问题看似简单但答案取决于你想要的效果是水平、垂直还是两者都要不同需求对应的方案完全不同。我把常见方案整理成了一张表需求方案适用场景水平居中text-align: center行内文本水平方向靠左/靠右text-align: left/right常规文本单行垂直居中容器高度 line-height按钮、标签多行整体垂直居中display: flex; align-items: center卡片、列表项上下左右任意定位position: absolute 父容器relative角标、蒙层文字精确居中Flex 三件套justify-content: center; align-items: center弹窗、空状态其中 absolute 定位 Flex 是组合拳。比如我要把一段文字放在容器的正中间可以这样.wrapper { position: relative; } .wrapper .center-text { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }translate(-50%, -50%)的作用是让元素自身的中点对准父容器中点这是 CSS 里最经典的精确居中方案不依赖元素自身尺寸。相比top: 0; right: 0; bottom: 0; left: 0; margin: auto那套写法transform 方案更直观也不会被 margin 影响。如果你做过那种固定画布尺寸的小游戏页面比如宽 1440px、高 810px 的植物大战僵尸风格仿写你会特别理解容器定位的重要性顶部得分栏、波次提示、暂停按钮、卡片栏的文本位置全都是靠着 absolute 定位 Flex 内部排版组合出来的。这类页面里最外层容器设position: relative内部每个界面元素用 absolute 钉在画布的具体坐标上元素内部的文字再用 text-align 或 Flex 对齐这样无论怎么改界面逻辑文字位置都稳如磐石。最后一个原则优先用文档流内方案实在搞不定再用 absolute。文本位置的问题80% 用 text-align 和 Flex 就能解决剩下 20% 才需要 absolute 出马。一上来就 absolute后面改布局会很痛苦。4. 高频小知识CSS 引入方式、单位选择与原子化 CSS4.1 CSS 引入的四种方式以及css 文件需要写