CSS3渐变与文本格式化实战:角度色标、截断与组合案例

发布时间:2026/11/3 20:35:48
CSS3渐变与文本格式化实战:角度色标、截断与组合案例

1. 从一次样式重构说起:为什么我把渐变和文本格式化放在一起看

CSS3渐变和文本格式化,这两个词单独拎出来都不算新东西,但组合在一起做页面时,很多人的第一反应是"写在静态样式里的"、"抄一段就能用"。我之前接手一个活动页改版,样式文件接近八千行,光渐变就有四十多处,写法五花八门:有的用linear-gradient(45deg, ...),有的用-webkit-linear-gradient(left, ...),还有几张渐变直接切成了 PNG 铺在上面。文本这边更乱,同一段说明文字出现了五种line-height,截断要么用text-overflow要么用 JS 算字数,标题的字间距每个模块都不一样。结果就是设计改一版,我要挨个模块去找哪段样式对应哪块内容,改到第三版的时候彻底崩溃,只能整体重构。

那次重构完之后我形成了一个习惯:只要页面里同时出现渐变和文本样式,就把它们放在一起审视。原因很简单,它们都属于"视觉细节层"的东西——不影响布局结构,却决定页面的第一眼质感;它们的写法都极其容易随手复制,一复制就埋坑;它们的兼容性坑又都集中在少数几个点上,摸清楚一次就能一直受益。所以标题里这个_04的编号我理解成一段学习路径的第四站,前面几站大概是基础选择器、盒模型、定位浮动这些,到这一站开始真正碰"好不好看"的问题了。

这篇文章适合两类人看。一类是刚把基础选择器和盒模型过完,准备写第一个像样页面的朋友,你在这里能拿到可以直接抄的渐变写法和文本处理套路;另一类是写了几年样式、但一直靠切图和 JS 解决文字问题的同行,这里关于角度换算、色标计算、多行截断边界条件的内容,大概率能帮你省下几张切图和一段脚本。我不打算把 MDN 的语法表重抄一遍,而是按我自己写代码时的实际顺序来讲:先讲为什么这么选,再讲参数怎么算,然后给完整案例,最后把踩过的坑摊开。

2. CSS3渐变的底层逻辑与角度色标算法

渐变这件事,最核心的认知转变是:它不是一张图片,而是一个由浏览器实时计算出来的背景层。这个认知决定了后面所有问题的解释方式——为什么渐变动画性能差,为什么打印时会消失,为什么角度写 45deg 出来的效果和你想象的不一样。只要把它理解成"浏览器按你给的函数,在元素盒子里铺了一层像素",很多疑惑自然就解开了。

我选择用原生渐变而不是切图,理由主要有三条。第一是分辨率,一张 750px 宽的渐变图放到 2 倍屏上会糊,而原生渐变在任何 DPR 下都是清晰的,这对大面积背景尤其明显。第二是改色成本,运营要改主题色,改一个色标值就是一秒钟的事,切图要走设计重新导出、上传、换 CDN 链接一整条链路。第三是体积,一个有五六个色标的复杂渐变,写成代码不到 200 字节,存成 WebP 也要几 KB。当然切图也有它的位置,比如那种带噪点、纹理、手绘笔触的复杂背景,CSS 硬写会非常恶心,这时候老老实实切图反而更划算。

2.1 线性渐变:角度与色标位置的真实计算

linear-gradient的语法看着简单,真正让人困惑的是角度。规范里0deg指向正上方,顺时针递增,所以90deg是从左到右,180deg是从上到下。但如果你是从老项目迁移过来的,会看到大量-webkit-linear-gradient(left, ...)这种写法,那个left指的是"起点在左边",等价于新的to right,等价于90deg。这三套写法混在一个项目里,是重构时最容易出错的地方。

关键词和角度有一个对应关系,我整理成表方便对照:

关键词写法等价角度视觉方向
to top0deg从下往上
to right90deg从左往右
to bottom180deg从上往下
to left270deg从右往左
to top right约45deg(随盒子比例变化)左下到右上

注意最后一行,to top right这个关键词的角度并不是固定 45 度,它会随元素宽高比自动调整,保证渐变线正好穿过两个对角。这一点很多人不知道,以为to top right就等于45deg,实际上在宽高比不是 1:1 的盒子里两者效果明显不同。

再说色标位置的百分比,这是最容易误算的一块。很多人默认百分比是相对元素宽度算的,其实不是,它是相对渐变线的长度算的。渐变线的长度怎么来?公式是这样的:设元素宽 W、高 H,渐变角度为 a(以标准角度制计),则渐变线长度 L = |W × sin(a)| + |H × cos(a)|。

举个具体例子。元素是 300px × 120px,角度写60deg。sin60 ≈ 0.866,cos60 = 0.5,那么 L = 300 × 0.866 + 120 × 0.5 = 259.8 + 60 = 319.8px。也就是说,当你在60deg的渐变里写50%,那个色标实际落在渐变线上 159.9px 的位置,而不是元素宽度的 150px。这个差异在窄高盒子里尤其明显,也是"我明明写的是 50%,为什么看起来偏了"的根本原因。

如果你需要让某个色标精确落在元素的某个像素位置,就得先把像素位置投影到渐变线上再换算成百分比。实操中我很少做这种精确计算,更常见的做法是用双位置色标语法把硬边钉死,比如:

.strip { background-image: linear-gradient(90deg, #ff6b6b 0 25%, #ffd93d 25% 50%, #6bcb77 50% 75%, #4d96ff 75% 100%); }

这种"一个色标写两个位置"的写法从较新的语法开始支持,表示这一段是纯色而非过渡。相比以前写#ff6b6b 0%, #ff6b6b 25%的笨办法,可读性高很多,改起来也不容易漏。用它做条纹、进度条刻度、分段占比图非常顺手。

关于透明度还有一个经典坑。你可能想做一个"从白色渐隐到透明"的遮罩,写成linear-gradient(to bottom, #fff, transparent)。问题在于transparent的计算值是rgba(0, 0, 0, 0),也就是透明的黑色。在支持预乘 Alpha 的现代浏览器里没问题,但在一些老引擎里,中间过渡段会往灰色、甚至发黑的方向偏。稳妥的写法是显式写同色透明:linear-gradient(to bottom, #fff, rgba(255, 255, 255, 0))。这个细节我在做图片底部渐隐遮罩时被坑过一次,遮罩中间出现一条脏灰色的带子,排查了半天才反应过来是transparent惹的祸。

2.2 径向渐变:形状、尺寸关键字与圆心控制

radial-gradient的参数比线性多,完整形式是"形状 尺寸 at 位置, 色标..."。形状只有circle和ellipse两种,不写会根据盒子形状自动判断。尺寸关键字有四个:closest-side、farthest-side、closest-corner、farthest-corner,默认值是farthest-corner。

这四个关键字的差别,用一句话概括就是"渐变圆的边缘落在哪个参照物上"。closest-side让圆的边缘刚好贴到离圆心最近的那条边,farthest-corner让它贴到最远的角。做内发光、聚光灯效果时,closest-side通常更合适,因为光晕不会溢出容器;做整体氛围背景时,farthest-corner能让四个角都覆盖到。

.spotlight { background-image: radial-gradient(circle closest-side at 50% 30%, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0) 100%); }

这段代码做的是一个顶部居中的柔光。这里我特意用了circle,因为如果让浏览器自动判断成椭圆,在宽盒子里光晕会被拉扁,看起来不自然。形状关键字该写就写,别省。

circle时只能给一个半径值,ellipse时给两个(水平半径、垂直半径)。用显式半径的好处是渐变范围不随容器尺寸变化,做固定大小的光斑时更可控:

.badge { background-image: radial-gradient(ellipse 60px 40px at 50% 50%, #ffd93d, #f6a623); }

位置参数at X Y支持百分比、长度和边缘关键字组合,比如at left 20px top 30%。做角标、小圆点装饰时,用at 100% 0把圆心放到右上角,能得到一个只露出四分之一的圆弧,比用border-radius加裁剪省事。

2.3 锥形渐变与重复渐变:少用但好用的两把刀

conic-gradient是围绕圆心按角度分布的渐变,语法是conic-gradient(from 起始角度 at 圆心, 色标...)。它最典型的用途是饼图、环形进度、色轮、加载动画。

.pie { width: 160px; height: 160px; border-radius: 50%; background-image: conic-gradient(#4d96ff 0 30%, #6bcb77 30% 55%, #ffd93d 55% 80%, #ff6b6b 80% 100%); }

这里色标的百分比是按角度算的,30% 对应 108 度。做环形进度条时,可以用conic-gradient加一个内圆遮罩,比 SVG 方案轻很多;但如果需要动画过渡,还是 SVG 的stroke-dasharray更顺滑,因为conic-gradient的角度值目前不能平滑插值。

repeating-linear-gradient和repeating-radial-gradient用来做重复图案,配合色标可以画出条纹、格子、斜纹布、进度条刻度。写重复渐变的关键是把周期控制好,最后一个色标的位置就是周期长度:

.diagonal-stripes { background-image: repeating-linear-gradient(45deg, #f5f7fa 0 10px, #e8eef6 10px 20px); }

这段是 45 度的细斜纹,周期 20px。这里有个性能提醒:重复渐变如果周期特别小(比如 2px),在低端设备上大面积铺开会有明显的绘制开销,尤其是在做滚动视差的时候。我一般会把周期控制在 8px 以上,更细的纹理宁可切一张小图平铺。

3. 文本格式化:真正拉开页面质感的那一层

说句实在话,大部分人学 CSS 的时候,文本属性是被跳过的部分。font-size、color、text-align三个一写,感觉就完事了。但我见过太多页面,布局结构没问题,配色也还行,就是看起来"不精致",追问下去往往是文本处理的问题:行高不统一、标题字距太挤、说明文字换行断得莫名其妙、长英文把容器撑破。这些细节单看都很小,累积起来就是专业和业余的差距。

我在处理文本时的基本策略是分三层。第一层是排版基础,字号、行高、字距、段落间距,这些定好之后整个页面的气质就稳了。第二层是溢出控制,什么时候换行、什么时候截断、截断用什么方式,这是防布局崩坏的保险。第三层是视觉修饰,阴影、描边、渐变文字,这些是锦上添花,但也是坑最多的。下面按这三层展开。

3.1 text-shadow 的光影分层写法

text-shadow的参数是offset-x offset-y blur-radius color,可以用逗号分隔写多层,从前往后依次叠加。它的能力边界很清晰:没有扩散半径(spread)、没有内阴影(inset)。这两点是从box-shadow转过来的人最容易踩的坑,因为box-shadow有第四个长度值和inset关键字,而text-shadow没有第四个长度值,如果你多写一个长度值,整条声明直接失效,而且不会报错,你只会看到阴影莫名其妙不见了。

做文字描边,text-shadow的经典做法是四个方向各偏移 1px 加零模糊:

.outlined-title { color: #fff; text-shadow: -1px -1px 0 #1f2937, 1px -1px 0 #1f2937, -1px 1px 0 #1f2937, 1px 1px 0 #1f2937; }

这个方案在字号小的时候会出现四个角的缺口,看起来像描边断断续续。补的办法是再加四个斜向偏移 1px 的阴影,凑成八方向,或者用-webkit-text-stroke(注意它画在字形内部,会吃掉笔画厚度,粗描边时字会变瘦)。我现在的习惯是:1px 描边用text-shadow八方向,2px 以上才考虑-webkit-text-stroke,并且配合增大字号来补偿视觉重量。

真正让我觉得text-shadow值钱的是做"柔和标题"而非硬描边。比如深色背景上放浅色标题,加一层低透明度、大模糊、轻微下偏的阴影,能让文字从背景里"浮起来",观感比纯色好很多:

.hero-title { color: #fff; text-shadow: 0 2px 12px rgba(0, 0, 0, 0.35), 0 1px 2px rgba(0, 0, 0, 0.2); }

两层阴影,第一层大模糊做环境光遮蔽,第二层小模糊补轮廓清晰度,这个组合我用了很久,几乎成了默认配方。要注意的是模糊半径越大,绘制成本越高,字数多、面积大的标题如果叠了三四层大模糊阴影,滚动时掉帧是有可能的,这种情况用一层就够了。

3.2 单行与多行截断:text-overflow 与 line-clamp 的边界

单行截断的三件套大家都背得出来:overflow: hidden、white-space: nowrap、text-overflow: ellipsis。但真正会出问题的场景是 Flex 布局里。把一段文字放进display: flex的子项,即使写了这三件套,它依然会撑开容器而不省略,原因在于 flex 子项的min-width默认是auto,它拒绝收缩到内容宽度以下。

解决办法有两个,一是给子项加min-width: 0,二是给子项加overflow: hidden(有overflow非visible时min-width的自动最小值也会失效)。我一般两个都写,因为有些场景里子项内部还有嵌套,只写一个不一定生效:

.list-item { display: flex; gap: 8px; } .list-item .title { flex: 1 1 auto; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }

多行截断现在主流还是用-webkit-line-clamp,配合display: -webkit-box和-webkit-box-orient: vertical:

.desc { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }

这个方案有边界条件需要注意。第一,它不适合用在display: flex或grid的容器上,会互相打架;第二,-webkit-box-orient在某些打包工具里会被优化掉,需要在注释里加autoprefixer: off之类的提示,或者改用行内样式写;第三,最后一行如果本身就很短,省略号可能出现得很突兀,这是机制决定的,无法精细控制。较新的标准属性line-clamp已经在部分浏览器支持,可以两个都写,向下兼容。

还有一种情况是多行截断加"展开更多",这里的取舍是:如果你需要动态判断文字是否真的被截断、以决定是否显示"展开"按钮,纯 CSS 是做不到的,得用 JS 比较scrollHeight和clientHeight。我的经验是,文字内容来自后端且长度不可控时,这个判断必须做,否则会出现按钮点了没反应的尴尬。

3.3 换行与断词:white-space、word-break、overflow-wrap 的分工

这三个属性经常被混着用,但职责完全不同。white-space管的是空白字符和换行符怎么处理,word-break管的是断词规则,overflow-wrap管的是"一个超长单词放不下时要不要强行断开"。

white-space的几个值里,实际用得最多的是normal、nowrap、pre-wrap。做用户评论展示时经常遇到用户输入换行但页面不换行的问题,这时候用white-space: pre-wrap就够了,它会保留换行和空格,同时超出容器时仍然自动换行。用pre的话就完全不自动换行了,长内容会横向滚动,除非你就是要做代码块。

word-break: break-all会把英文单词从中间切断,用于处理用户输入的连续字符、URL、订单号这类东西非常有效,但用在正常中文段落里会让标点跑到行首,排版变丑。中文场景下更好的选择是overflow-wrap: break-word(旧名word-wrap: break-word),它只在单词长到一行放不下时才断,正常句子不受影响。

.comment { white-space: pre-wrap; overflow-wrap: break-word; word-break: normal; }

这三行我基本当成用户生成内容的默认配方。另外提一句word-break: keep-all,它禁止在 CJK 文本内部断行,只允许在空格处断,做中英混排的标题时偶尔会用到,但用不好会出现一整行被撑爆的情况,谨慎。

还有一个细节是连字符。hyphens: auto会根据lang属性做英文断词并加连字符,观感比硬断好看,但必须设置正确的lang,否则不生效。我做多语言站点时才用它,单语言项目里收益不大。

3.4 字距、行高与文本对齐的微调尺度

letter-spacing有个几乎所有人都忽略的副作用:它会在最后一个字符后面也加一份间距,导致居中的文本视觉上偏左。给标题加了 0.1em 字距之后感觉"没居中",就是这个原因。修法是加一个等量的负margin-right,或者用text-indent抵消:

.section-title { text-align: center; letter-spacing: 0.08em; margin-right: -0.08em; }

line-height建议永远写无单位数值,比如1.6,这样它会基于元素自身字号计算。如果写24px,子元素继承下来就是固定的 24px,一旦子元素字号变大就会挤在一起;写1.6em也有类似问题,因为em在继承时已经被解析成像素了。这是 CSS 里最经典的一个继承陷阱,我见过太多项目在按钮里写line-height: 40px,然后所有图标文字都跟着偏移。

text-align: justify在中文段落里效果不错,但中英混排时会出现单词间距忽大忽小的"河流"现象。要缓解的话,得配合text-justify: inter-ideograph和hyphens,但支持度参差。我的做法是正文一律左对齐,只有需要强调排版感的介绍段落才用两端对齐。

最后说text-decoration的简写。现在可以把线型、样式、颜色、粗细一起写:text-decoration: underline wavy #ff6b6b 2px。做链接下划线时配合text-underline-offset把线往下推 2 到 4px,观感立刻从"浏览器默认"变成"设计过":

a.link { text-decoration: underline; text-decoration-color: rgba(77, 150, 255, 0.4); text-decoration-thickness: 2px; text-underline-offset: 4px; } a.link:hover { text-decoration-color: #4d96ff; }

这一个小改动,比换字体、换颜色的效果都明显。

4. 组合实操:三个可直接抄的落地案例

理论说完了,接下来是我实际项目里反复用到的三个组合方案。每一个都涉及渐变和文本格式化的配合,也都在真实场景里被反复打磨过。

4.1 渐变按钮:含悬停过渡与降级处理

按钮是最需要渐变的组件。直接给background-image加渐变,然后在:hover里换另一组色标,理论上可行,但会出现生硬的跳变,因为渐变图像之间不能平滑过渡。我试过几种方案,最后稳定下来的是"双层背景 + 透明度过渡":

.btn { position: relative; display: inline-flex; align-items: center; justify-content: center; height: 44px; padding: 0 28px; border-radius: 8px; font-size: 15px; font-weight: 600; letter-spacing: 0.05em; margin-right: -0.05em; color: #fff; background-image: linear-gradient(135deg, #4d96ff, #6bcb77); transition: transform 0.2s ease, box-shadow 0.2s ease; } .btn::after { content: ""; position: absolute; inset: 0; border-radius: inherit; background-image: linear-gradient(135deg, #6bcb77, #4d96ff); opacity: 0; transition: opacity 0.25s ease; } .btn:hover::after { opacity: 1; }

要点拆解一下。第一,悬停的另一组渐变色放在伪元素上,用opacity过渡,浏览器只需要合成,不触发重绘,性能好。第二,inset: 0是top/right/bottom/left的简写,继承border-radius保证圆角一致。第三,按钮文字要盖在伪元素之上,所以伪元素用了::after且没有z-index冲突时,文字默认在上层;如果项目里有奇怪的层级问题,给文字包一层span加position: relative; z-index: 1。

降级方面,我习惯用@supports兜一层:

@supports not (background-image: linear-gradient(135deg, #000, #fff)) { .btn { background-color: #4d96ff; } }

现代浏览器基本都支持,这层只是保险。但真正需要保险的是打印样式,很多人不知道打印时浏览器默认不输出背景色和背景图,渐变按钮在打印预览里会变成白底白字。修法是在打印媒体查询里加-webkit-print-color-adjust: exact; print-color-adjust: exact;,或者干脆在打印样式里把渐变换成深色文字。

4.2 渐变描边卡片与玻璃质感

带渐变边框的卡片,早期做法是外层渐变背景加内层白底,中间留 1px 缝隙。现在更优雅的写法是用双背景加background-clip:

.grad-card { position: relative; padding: 1px; border-radius: 14px; background-image: linear-gradient(135deg, rgba(77, 150, 255, 0.6), rgba(107, 203, 119, 0.1) 50%, rgba(255, 217, 61, 0.5)); } .grad-card .inner { padding: 20px; border-radius: 13px; background-color: #fff; }

外层负责渐变,内层负责内容,圆角差 1px 正好对应描边宽度。这个方案的好处是渐变描边可以有透明度变化,比实线边框有层次感,而且padding: 1px就是边框宽度,改起来直观。

玻璃质感卡片在此基础上加backdrop-filter:

.glass-card { border: 1px solid rgba(255, 255, 255, 0.35); border-radius: 16px; background-image: linear-gradient(135deg, rgba(255, 255, 255, 0.25), rgba(255, 255, 255, 0.05)); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); box-shadow: 0 8px 32px rgba(31, 41, 55, 0.12); }

这里有个性能经验:backdrop-filter作用区域越大越吃 GPU,如果卡片铺满整屏并且页面还有滚动动画,低端安卓机上会明显掉帧。我的做法是限制模糊半径在 12px 以内、卡片面积不超过视口的三分之一,超出就走半透明纯色降级。另外backdrop-filter在某些浏览器里会创建一个新的层叠上下文,导致内部的position: fixed元素定位异常,这个坑排查起来很费时间。

玻璃卡片上的文字,颜色和阴影要重新调。原来的深色正文放在半透明背景上会显得发灰,通常要把文字颜色往深里压一档,再加一层极淡的文字阴影提升可读性。

4.3 渐变文字标题与长文本截断组合

渐变文字是这几年很常见的标题样式,核心三行:

.grad-text { display: inline-block; font-size: 32px; font-weight: 700; letter-spacing: 0.02em; background-image: linear-gradient(90deg, #4d96ff, #6bcb77 60%, #ffd93d); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: transparent; }

几个必须注意的点。第一,display: inline-block不能省,纯inline元素上做background-clip在部分浏览器里裁剪区域会算错,文字换行时尤其明显。第二,-webkit-text-fill-color: transparent和color: transparent要一起写,前者优先于后者,只写color在旧版 WebKit 里不生效。第三,渐变文字的渐变方向建议用90deg或135deg,如果字很长,用横向渐变能让颜色过渡更均匀;如果只有两三个字,135deg的对角过渡更有层次。第四,别忘了选中文字,透明填充会让用户选中时看不清,加一条::selection规则把背景和颜色定死:

.grad-text::selection { background: #4d96ff; color: #fff; -webkit-text-fill-color: #fff; }

标题下面通常跟一段说明文字,做 2 行截断,这时候要注意行高和line-clamp的配合。line-clamp计算的是行数,不是高度,所以容器高度不用写死,但父级如果用了固定高度做对齐,要确保它足够容纳 N 行乘以行高:

.card h3 { font-size: 17px; line-height: 1.5; margin: 0 0 6px; } .card p { font-size: 13px; line-height: 1.6; color: #6b7280; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }

这套组合我在列表页里用了很多次,卡片高度由标题一行、描述两行、底部信息一行决定,非常规整,不会因为后端返回的文字长短不一而错位。

5. 常见问题与排查速查

下面这些是我在实际项目里真真切切踩过、并且被同事问过不止一次的坑。整理成表格,方便你遇到问题时直接对号入座。

5.1 渐变相关的高频坑

现象可能原因解决方式
渐变完全不显示整条声明被浏览器丢弃,通常是角度位置写了多余关键字或单位错误用开发者工具看该属性是否被划掉,逐段精简测试
渐变中间出现灰黑色脏带用了transparent,老引擎按透明黑插值换成同色透明,如rgba(255,255,255,0)
45deg 效果和设计稿对不上设计稿按元素宽度标色标,未考虑渐变线长度用 `
悬停切换渐变色生硬渐变图像之间无法平滑过渡双层背景加伪元素opacity过渡
打印后渐变消失打印默认不输出背景加print-color-adjust: exact或改打印样式
大面积重复渐变卡顿周期过小导致绘制开销高周期拉到 8px 以上,或改用背景图平铺

关于表格里第一条我多说两句。CSS 里语法错误不只是"这一行不生效",整条声明都会被丢弃,而浏览器控制台又不会报错,所以你只能看到"没效果"。排查渐变失效时,我的固定做法是:先写最简单的两色渐变,确认能出来,再逐段加参数。如果一上来就复制了一段十层的复杂渐变,出问题时你根本不知道是哪一层语法错了。

还有一个隐蔽的失效场景:给元素同时写了background-color和background-image,然后background-image那一行被后面的background简写覆盖了。background简写会把所有子属性重置,包括你没写的那些。所以简写和子属性不要混在一个选择器里随机排列,要么全用简写,要么全用子属性。

5.2 文本格式化相关的高频坑

现象可能原因解决方式
单行省略号不生效元素在 flex 子项里且未设置min-width: 0补min-width: 0和overflow: hidden
多行截断后省略号位置怪异line-clamp最后一行本身较短属机制限制,必要时改 JS 计算
text-shadow加了四个长度值失效该属性不支持扩散半径删掉第四个长度值
中文段落断行断到标点后word-break: break-all生效中改为overflow-wrap: break-word
用户输入换行显示成一整行white-space为默认normal改white-space: pre-wrap
加了字距后居中偏左最后一个字符后也产生间距补负margin-right抵消
子元素行高诡异错位父级用了带单位的line-height改成无单位数值

多行截断那条我需要强调一下:这是纯 CSS 方案固有的局限,不是写法问题。如果你对省略位置的精确度要求很高,比如必须保证最后一行至少显示若干个字,那就得用 JS 做字符数估算,或者让后端在存储时截好。这个取舍我在不同项目里做过不同选择:内容运营后台可控的,用纯 CSS;用户自由输入的,宁可多一行高度也不截断。

5.3 兼容性与性能的取舍记录

兼容性这块,我现在的底线策略是:linear-gradient和radial-gradient基本当基线能力用,不做前缀;conic-gradient用之前查一下目标机型,做进度指示时准备纯色降级;background-clip: text要带-webkit-前缀,因为标准background-clip: text的支持来得晚;line-clamp标准属性和-webkit-版本一起写。

性能方面,有三条实测经验。第一,渐变本身是静态背景,浏览器会缓存成纹理,滚动时不重绘,成本很低;但如果你用background-position或background-size做动画,就会每帧重绘,大面积元素上明显掉帧,改成transform位移一个更大的渐变层会好很多。第二,多层text-shadow的成本和层数乘以模糊半径正相关,标题上三四层大模糊阴影,在长列表滚动时是能感觉出来的。第三,backdrop-filter和filter会触发离屏渲染,嵌套使用时开销叠加,能不平铺就不平铺。

提示:调试渐变时把background-image单独抽到一个测试类里,一次只加一个色标,比在完整样式里盲改效率高出几倍。

最后分享一个我自己在用的调试小技巧。渐变和文字的问题,很多是"看不出来哪一层出错",所以我习惯临时加一层outline: 1px solid red把元素边界显出来,特别是做渐变文字时,你能立刻看出background-clip的裁剪范围到底是不是元素盒子,还是只裁到了文字块的一部分。这个动作花三秒钟,能省下十几分钟的猜测。

我个人在实际操作中的体会是,这两个主题真正难的地方不在于记住语法,而在于建立"渐变是一层要计算的像素、文本是一层要防守的排版"这种意识。有了这个意识,看到效果不对时,你会本能地去算角度、去查属性有没有被简写覆盖、去想 flex 的自动最小宽度,而不是漫无目的地试数值。