尧图精选

CSS字体样式全攻略:从基础避坑到渐变描边与实战速查

🕒 发布时间:2026/9/17 12:37:11 📁 来源:尧图网络
做前端也好写静态页面也好CSS 字体样式永远是绕不开的一块。你可能觉得 font-size、font-weight 这些属性太基础了可真到了实际项目里中文混排对不齐、行高像幽灵一样乱跑、input 里的字死活不居中、想给标题加个描边却总差那么点意思这些问题一抓一大把。这篇就把 CSS 字体样式从最基础的 font-family 到渐变文字、外描边、删除线这些花活再到各种居中、省略、hover 交互全部用能直接跑起来的代码和踩坑记录整理一遍。刚入门的同学能按顺序跟下来有经验的朋友也可以直接跳到后面看速查表。1. 字体样式的基础认知与避坑清单1.1 字体族font-family 为什么总是“失灵”刚开始学 CSS 的时候我一度以为 font-family 写一个字体名就完事了。后来才明白这个属性本质上是让浏览器按照你给的列表“逐个尝试”直到找到系统里真实存在的那一个字体。body { font-family: PingFang SC, Microsoft YaHei, Noto Sans CJK SC, sans-serif; }上面这段的意思就是在 Mac / iOS 上优先用苹方在 Windows 上优先用微软雅黑如果都没有就用系统默认的无衬线字体。很多人所谓的“font-family 失灵”其实是只写了一个字体名比如只写“微软雅黑”到了 Mac 上就回退成了默认字体观感立刻变丑。中文字体在不同操作系统里的名字差异很大所以正确的做法是写一组完整的字体栈把英文、中文、通用字体族依次排开。还有一个很容易被忽略的点英文字体要放在中文字体前面。因为中文字体里往往也自带了英文字形但很多中文字体设计的英文数字不太协调比如全角数字和半角数字混在一起。所以更好的写法是类似这样body { font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, PingFang SC, Microsoft YaHei, sans-serif; }另外字体名如果包含空格比如 “Times New Roman”必须用引号括起来。这个引号在 Windows 下有时候省略了也能跑到但在某些 Linux 环境或者特殊字符场景下是真的会解析失败。所以我个人建议所有自定义字体名和带空格字体名都统一加引号不要赌浏览器心情。1.2 font-size 与 line-height行高的继承陷阱font-size 比较好理解px、em、rem、vw 这些单位按场景选就行。真正让人头疼的是 line-height行高它直接决定一段文字在页面上占多高也决定文字能不能垂直居中。关键知识点是line-height 建议用无单位的数字比如 line-height: 1.5而不是 px 或者百分比。为什么因为无单位数字是“因子”子元素会根据自身的 font-size 重新计算行高。而如果你写下的是 line-height: 150%浏览器会把父元素计算出来的行高值先算成一个固定像素值然后这个像素值再被后代继承导致子元素字号变大时行高却没有跟着变。我举一个实际例子。假设 body 的 font-size 是 16pxline-height 设成 1.5那正文里一个 p 标签如果自己设了 font-size: 20px它的实际行高就是 20 * 1.5 30px。但如果 body 写的是 line-height: 150%p 标签即使字号是 20px继承到的行高还是父元素算出来的 16 * 1.5 24px而不是 30px。这个差距在中文段落排版里非常明显行高看起来会“憋得慌”。另一个常见踩坑是单行文本垂直居中。很多教程会说“height 和 line-height 保持一致就能垂直居中”这句话只对单行文本成立。一旦文字换行变成两行这个方案立刻失效文字会溢出容器或者贴到最上方。所以现在我做单行居中时依然会用 line-height但只要涉及多行文本就直接改用 flex 或者 Grid 去处理对齐。1.3 font-weight 与 font-style字重不生效的真相font-weight 最常用的是 normal400和 bold700CSS 里其实支持 100 到 900 的数值数值越大越粗。但有个隐藏前提系统里安装的字体本身必须提供对应的字重文件。如果你用的字体只有 Regular 和 Bold 两个文件那么 font-weight: 600 这个“中间态”浏览器往往会自行映射到 400 或 700看上去根本没变粗。更麻烦的情况是 font-weight: bold 完全不生效多半是因为你通过 font-face 引入的字体文件只有一份 400 字重浏览器找不到 700 就会用普通字形硬撑视觉上几乎没变化。解决方法是给 font-face 分别声明 400 和 700 两个来源文件或者干脆用支持可变字重variable font的字体。font-style 这边要区分 italic 和 oblique。italic 是“斜体字形”很多英文字体专门设计了一套倾斜笔画而 oblique 是“把正常字形强制倾斜”。中文场景下绝大多数中文字体没有真正的 italic 字形所以 em 标签在中文里呈现的基本就是浏览器做的伪倾斜。这里有个 CSS 属性叫 font-synthesisnone可以禁止浏览器自动合成伪粗体和伪斜体。如果你引入了某个字体结果发现它被强行拉斜压扁先检查一下是不是这个原因。2. 字体效果的进阶玩法渐变、描边与删除线2.1 渐变文字background-clip 的冷门用法文字渐变其实不是直接给字体加颜色而是把背景图裁剪到文字形状上。核心是 background-clip: text 这个属性。实现代码很简单.gradient-text { background: linear-gradient(90deg, #ff6a00, #ee0979); -webkit-background-clip: text; background-clip: text; color: transparent; }原理就是把 background 画在元素背景层然后用文字作为蒙版把文字以外的部分裁掉最后把文字本身的颜色设为 transparent这样露出来的就是背景渐变。这个玩法在 Chrome、Safari、Firefox 里都能跑但一定要写 -webkit- 前缀因为很多现代浏览器仍然认这个旧前缀。这里有一个必须注意的坑如果浏览器不支持 background-clip: text而且你又设置了 color: transparent那文字就会直接“消失”。所以我一般会这么处理.gradient-text { color: #ff6a00; } supports (-webkit-background-clip: text) { .gradient-text { background: linear-gradient(90deg, #ff6a00, #ee0979); -webkit-background-clip: text; background-clip: text; color: transparent; } }用 supports 检测一下不支持的环境保留一个兜底颜色。渐变文字适合大标题、品牌词、活动页核心文案但不适合大面积正文字体因为可读性和加载成本都不划算。2.2 文字描边与外描边从 -webkit-text-stroke 到 text-shadow文字描边有一个“不太标准但很好用”的属性-webkit-text-stroke。说它不标准是因为它带着前缀但 Chrome、Safari、Firefox 目前都支持所以在实际项目里可以直接用。.outline-title { -webkit-text-stroke: 2px #1a1a1a; color: #fff; }这样就能得到一个白色填充、黑色描边的文字视觉上类似综艺标题。但要注意-webkit-text-stroke 的描边是沿着文字笔画中心线双向延伸的一半在内一半在外。笔画密集的中文字在这种效果下很容易被描边“糊住”笔画间隙变得很脏。所以中文字号比较小的时候我不太推荐用粗描边笔画之间会粘连成一团。如果要做真正的“外描边”也就是描边完全出现在文字笔画轮廓外侧更常用的做法是用四方向或八方向的 text-shadow 模拟.outline-shadow { color: #fff; text-shadow: 1px 0 0 #1a1a1a, -1px 0 0 #1a1a1a, 0 1px 0 #1a1a1a, 0 -1px 0 #1a1a1a, 1px 1px 0 #1a1a1a, -1px -1px 0 #1a1a1a, 1px -1px 0 #1a1a1a, -1px 1px 0 #1a1a1a; }这种方式的好处是描边始终在文字外部不侵占笔画内部空间中文小字号下也更干净。缺点是 text-shadow 数量多了以后性能开销会增加尤其在低端手机或者频繁动画的情况下。另外 text-shadow 不会影响布局描出来的边可能会和旁边的元素挨得太近需要自己加 padding 留出呼吸空间。2.3 下划线、删除线与段落分隔线的正确做法text-decoration 这个属性现在已经被拆成多个子属性了text-decoration-line 控制下划线、删除线、上划线text-decoration-style 控制线型text-decoration-color 控制线的颜色text-decoration-thickness 控制线粗。业务里最常见的删除线场景是价格划线比如原价被划掉。.price-old { text-decoration: line-through; text-decoration-color: #999; text-decoration-thickness: 2px; }如果只写 text-decoration: line-through线的颜色默认跟随文字颜色有时候你只想让删除线变浅一点、文字颜色保持不变就必须单独设置 text-decoration-color。这个细节很多人第一次都会漏。“段落分割线”这个需求我经常看到有人用一整行hr或者一个空 div 加 border 来实现。简单一点的做法确实是用 border-bottom但如果你希望分割线和文字之间有一点间距或者只在某一段落后面出现用伪元素更灵活.divider::after { content: ; display: block; border-bottom: 1px solid #e5e5e5; margin-top: 24px; }还有就是链接下划线的问题。浏览器默认会给 a 标签画一条下划线但在现代设计里默认下划线通常太贴字、太细。可以用 text-underline-offset 调整下划线离文字的距离再用 text-decoration-thickness 控制粗细效果立刻立体很多。3. 文本排版与对齐垂直水平居中的真正解法3.1 文本水平垂直居中的四种姿势“怎么让文字垂直水平居中”是我见过被问得最多的问题没有之一。我把常用的四种方法按场景列一下。第一种单行文本最简单给容器设置 height再让 line-height 等于容器高度同时加 text-align: center。这是最省事的方案但只适用于一行文字而且高度必须固定。第二种多行文本尽量用 flex。这是我最常用的方案父容器 display: flex然后 align-items: center 控制垂直方向justify-content: center 控制水平方向。它不依赖固定高度文字多了容器会自动撑开也不会像 line-height 方案那样出现多行错位。.box { display: flex; align-items: center; justify-content: center; }第三种是 Grid 布局其实原理和 flex 很像可以写 place-items: center适合整个页面级别的大区域居中。第四种是 absolute 定位加 transformtop: 50%; left: 50%; transform: translate(-50%, -50%)。这个方案适合在不知道容器宽高、但又必须精确定位在某一点的场景比如弹窗里的提示文字。还有一个容易混淆的属性是 vertical-align。它只对行内元素、行内块元素以及表格单元格生效对 div 这种块级元素一点用都没有。很多人给 div 写了 vertical-align: middle看不到效果就一脸懵其实方向就错了。3.2 input 与按钮中的文字居中问题input 和 button 的文字居中堪称“重灾区”。先说 input 本身。原生 input 在不同浏览器里默认有 padding、border、高度各种差异直接设置 height 加 text-align: center 往往并不理想。因为在部分浏览器里input 的行高并不受 line-height 完全控制文字会明显偏离视觉中心。我的做法是先把 input 的默认 padding 重置掉然后用 padding 来撑出高度而不是依赖 height.input-center { text-align: center; padding: 10px 16px; border: 1px solid #ccc; border-radius: 6px; font-size: 16px; }这样文字在水平和垂直方向上都由 padding 控制基本不会偏移。如果里面是 placeholder它的对齐方式继承 input 的 text-align所以也一样能实现居中。然后再说“让整个 input 在页面或容器里居中”这个需求那就不是 input 内部文字的问题了直接用父容器 flex 把 input 包起来就完事。button 按钮里文字垂直居中相对简单一些。如果用的还是默认 button 样式试试加上 display: inline-flex; align-items: center; justify-content: center这样即使内容换成带图标的 flex 布局也不会乱。table 单元格里也是一样的逻辑text-align: center 控制水平vertical-align: middle 控制垂直这两兄弟只在 table-cell 场景下配合默契。3.3 中文段落排版缩进、间距、省略号与多行截断中文排版和英文排版有一些差异很值得注意。中文首行缩进应该用 text-indent: 2em这个 em 是相对当前字号而言的所以文字字号变化后缩进依然保持两个中文字符的宽度。很多人会手动敲两个空格代替这在响应式布局里基本是灾难建议直接改正。letter-spacing 可以用来控制字与字之间的间距但中文字体通常不需要额外加太大字距加个 1px 到 2px 就会显得散。word-spacing 只对英文单词之间的空格生效中文场景基本用不到。与之相关的还有 text-align: justify它能实现两端对齐让段落右侧边缘更整齐。中文段落如果不用 justify因为汉字宽度固定右边缘会呈锯齿状尤其在大段文字里特别明显。单行省略这个老生常谈的写法是.ellipsis { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }注意它要求元素本身是 block 或 inline-block并且有确定的宽度或 max-width。多行省略更常用的是 -webkit-line-clamp。比如两行超出显示省略号、三行超出显示省略号核心代码是.clamp-2 { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }这个实现依赖 WebKit 的盒模型但在 Chrome、Safari、Firefox 现代版本里实际都能用。要注意的是如果容器同时设置了 padding省略号的位置可能会受到 padding 影响出现最后一行文字被截断得比较突兀的情况。如果要求特别严格可以把 padding 移到内层子元素上外层专门负责裁剪。4. Web Font 与字体引入让字体加载不再拖慢首屏4.1 font-face 与现代字体格式当你不满足于系统自带字体想用 Inter、Roboto Mono 这类西文字体时就得用 font-face 自己声明字体。现代浏览器基本只需要 WOFF2 格式压缩率高加载快。传统上为了让老浏览器兜底可能还要放 WOFF 甚至 TTF但今天除非你有明确兼容要求只提供 WOFF2 就足够了。一个典型的 font-face 写法长这样font-face { font-family: Inter; src: url(./fonts/Inter-Regular.woff2) format(woff2); font-weight: 400; font-style: normal; font-display: swap; }这里有个容易忽略的细节如果同一个字体族你有 400 和 700 两份文件必须写两个 font-face 块分别声明 font-weight并且 font-family 保持一致。否则页面里用到 font-weight: 700 的地方浏览器找不到对应文件就会出现“明明设置了加粗但没变化”的情况。font-display 这个属性也值得花点篇幅聊。swap 是默认很多字体服务的策略意思是字体文件加载期间先用默认字体显示文字文件加载完再切换成目标字体这就会造成文字闪烁FOUT。把 font-display 改成 optional 可以在字体加载慢的时候放弃使用减少闪烁但如果加载速度足够快它又能正常应用。我的经验是对于非关键装饰性文字optional 更省心。4.2 中文字体子集化与按需加载中文字体和西文字体最大的区别就是字符集巨大。一个完整的中文字体文件动辄几 MB直接扔给 CSS font-face 会严重拖慢首屏加载这也是为什么很多中文站点最终都选择“不清真”地用系统字体。如果想用特定设计字重做中文字体比较靠谱的思路是“子集化”。也就是从完整字体重抽取页面真正用到的文字生成一个小体积的字体文件。比如页面标题只用了几十个字那这把子集字体可能只有几十 KB加载起来完全没压力。常用的工具像 fontmin、glyphhanger 都能做这件事但一定要确认你有该字体的使用授权尤其商业项目版权问题远比性能问题棘手。另一种方式是利用 unicode-range 让浏览器按需下载字体的某个切片。很多在线字体服务会按拉丁字母、数字、常用标点、常用汉字等不同区间把字体拆成多个文件页面里用到哪个区间才下载哪段。这个技术思路很好但如果你只是部署一个简单的静态页面我更推荐直接用系统字体栈或者只加载少量文字做成图片 / SVG。中文网页渲染性能的优先级永远优先于“某个特殊字重”。4.3 样式引入方式外部link、内联style与import怎么选“CSS 样式引入方式”是入门必考题大概分四种内联样式、内部 style 标签、外部 link 样式表、以及 import。它们优先级也不同内联样式最高后面三者取决于顺序和选择器权重。生产环境我几乎只用外部样式表link relstylesheet href./style.css /好处是浏览器会缓存样式文件第二次访问快很多代码也容易复用。import 的问题在于它会串行加载 CSS会阻塞页面解析对性能不友好。除非做一些特殊的小模块复用否则我不建议在主业务里用 import。内联样式虽然可以直接给单行字体加样式但不利于维护也会让样式优先级变得很难排查。如果你在 HBuilder 里写 CSS 没有智能提示多数情况是没把 CSS 文件用 link 正确引入编辑器识别不到 CSS 文件的上下文和属性本身没关系。5. 交互与动画字体样式在动效中的实战技巧5.1 hover 鼠标移入的文字反馈鼠标移入文字后的反馈是提升界面质感的最快手段。最基础的自然是改颜色、改字重.nav-link { color: #333; transition: color 0.25s, letter-spacing 0.25s; } .nav-link:hover { color: #ff6600; letter-spacing: 0.3px; }但这里有一个非常重要的踩坑点hover 时修改 font-size 或 font-weight 会引起文字宽度变化进而导致旁边元素跳动。尤其是在导航栏这种水平排列的容器里一个菜单项字体变宽了其它项就会跟着歪。如果想保留加粗效果可以设置一个固定的占位宽度或者用 transform: scale 放大而不是直接改字号。还有一个思路是使用可变字体在 hover 时平滑改变字重数值宽度变化也由字体系统重新计算视觉更顺滑但这个方案依赖浏览器支持和你是否有可变字体文件。另一个常见需求是“鼠标移入时下划线从左到右划出来”。text-decoration 的 transition 效果非常有限真正自然的做法是用背景渐变模拟下划线长度动画.link { background-image: linear-gradient(currentColor, currentColor); background-repeat: no-repeat; background-position: left bottom; background-size: 0% 2px; transition: background-size 0.3s; } .link:hover { background-size: 100% 2px; }用 currentColor 的好处是下划线颜色永远和文字颜色保持一致不管文字颜色怎么变都不用单独维护样式。5.2 文字变形、发光与打字机效果文字相关动画除了 color 过渡还可以玩渐变背景位移、发光、打字机等效果。具体能做成什么取决于想象力我要分享两个常用模板。第一个是渐变文字加流动光效。做法和 2.1 的渐变文字一样只是把 background 的尺寸放大两倍然后让 background-position 动起来.flow-text { background: linear-gradient(90deg, #ff6a00, #ee0979, #ff6a00); background-size: 200% 100%; -webkit-background-clip: text; background-clip: text; color: transparent; animation: flow 3s linear infinite; } keyframes flow { to { background-position: -200% 0; } }这里背景渐变两段是头尾同色动画位移 200% 后循环起来视觉上没有断点看起来光斑一直在标题上流动。第二个是文字发光。用多层 text-shadow 模拟霓虹灯效果再配合透明度或滤镜动画做呼吸感。不过我要提醒一句text-shadow 的多层使用在小面积标题上没问题但不要在大段正文上做性能不是这么浪费的。动效方面尽量用 opacity 和 transform 这类不触发重排的属性对于文字本身能用 color 过渡就不去频繁改 text-shadow。5.3 导航栏与Tab控件里的文字对齐细节导航栏和 Tab 控件是字体样式最容易暴露问题的地方。首先是文字和图标对不齐。导航项里经常有一个 icon 加一个文字默认情况下 icon 的基线可能和文字基线不一致。我习惯把图标用 span 包起来设置 display: inline-flex; align-items: center然后让文字也用 line-height 和 padding 控制而不是靠 vertical-align 去强行微调。Tab 控件的激活态文字一般会变粗变色。如果没有处理宽度变化激活状态会让 Tab 宽度变宽整排控件跟着晃。常见处理方式有两种一是给所有 Tab 预留相同的 padding 和 min-width让变化在内部消化二是激活态不改字重只改颜色加下划线简单又稳定。字体样式这块很多时候“克制”比“炫技”更重要。导航栏里的字用户最在意的是清晰、不跳、好点。6. 常见问题排查与速查表6.1 高频报错与奇怪现象定位下面这个表格直接把我在项目里遇到最多的问题按“现象、原因、解决办法”整理出来写样式时对着查很方便。现象可能原因解决办法font-weight: bold 没变化字体文件没有对应粗体浏览器只能拿默认字重硬撑给 font-face 配置 400/700 两份文件或换用多字重/可变字体段落行高看起来不一样继承了固定 px 或百分比行高子元素字号变化后行高没重算在父元素使用无单位 line-height如 1.6渐变文字不显示浏览器不支持 background-clip: text或 color: transparent 之后背景没有渲染用 supports 做能力检测并提供纯色兜底删除线颜色不是想要的颜色未单独设置 text-decoration-color加上 text-decoration-color 和 thickness两行省略号失效缺少 display: -webkit-box 或 overflow: hidden或容器没有宽度约束确认三件套完整display / box-orient / line-clampinput 文字不居中浏览器默认 padding 和 line-height 处理不同重置 padding用 padding 撑高或父容器用 flex导航 hover 时宽度跳动字重/字号变化导致文字宽度变化改颜色和装饰线或使用可变字体文字描边把笔画糊住-webkit-text-stroke 是中心线描边中文密集笔画会粘连改用 text-shadow 外描边或加粗笔画间距import 的样式加载很慢import 会串行下载并阻塞渲染改成link relstylesheet6.2 我的学习路径建议与最后提醒如果你刚开始啃 CSS 字体这部分别一上来就玩渐变文字和描边字。先把 font-family 的字体栈写明白把 line-height 的继承关系吃透再掌握单行和多行的居中与截断这些基础能解决日常 80% 的样式的幺蛾子。之后再慢慢往渐变、描边、动画和字体加载优化上扩展每个新特性都先查一下浏览器兼容性再决定用不用。我自己的习惯是遇到字体样式不对第一件事不是去 CSS 文件里盲改而是打开浏览器开发者工具看“计算样式”面板里最终生效的 font-family、line-height、color 分别是什么。很多时候问题出在优先级被覆盖或者某个字体名拼写错误计算样式面板一眼就能看出来。另外中文 Web Font 的水很深如果项目对性能敏感能少用就少用系统字体栈配好了一样能获得很专业的观感。最后再分享一个小技巧写完多行省略号以后在 Firefox 里也测一下因为有些版本的 Firefox 对 -webkit-box 前缀属性的支持和 Chrome 略有差异。字体样式看着小想要稳还是得在多个环境里过一遍才行。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →