
一晚上盯着屏幕把能搜到的文字特效一个一个扒下来、改参数、截图对比最后浏览器里开着四十八个标签页——这就是这篇东西的由来。四十八个超炫酷的CSS 文字特效看着像四十八份代码实际上把它们摊开比对一遍就会发现真正独立的底层技法只有七种剩下全是参数组合。很多人收藏了一堆特效代码真到项目里用的时候却卡在为什么我这里描边糊了为什么渐变字在同事电脑上是黑块这种问题上根子就在于只抄了结果没搞清文字在浏览器里是怎么被画出来的。下面这些内容既包含四十八个效果的分类清单和可复制的核心代码也有我踩过的兼容坑、性能坑和字体坑前端新手能照着抄做了一两年的人也能拿去重新梳理自己的样式库。1. 先搞清浏览器给文字上色的三层结构不然特效全是玄学1.1 文本层、字形层与绘制层三个概念决定了特效能不能生效浏览器渲染一行文字粗略可以拆成三层来看。第一层是文本内容层也就是你写在 HTML 里的字符本身它决定了行高、换行、基线和可选中范围第二层是字形轮廓层字体文件里每个字符的矢量路径就是这一层描边、镂空、外发光这类沿着字形边缘做文章的效果全部作用在这里第三层是绘制层也就是文字被填充成什么颜色、什么图案渐变字、图片填充字、纹理字属于这一层。为什么要先分清这个因为很多特效失效本质是你把它加在了错误的层上。比如你想让文字有渐变正确做法是把背景画在绘制层再用background-clip: text裁切但如果你给一个span设置了display: block之后忘了它已经变成了一个块级盒子背景会先铺满整个盒子宽度渐变的位置就完全不是你想要的那样了。同样-webkit-text-stroke作用在字形轮廓层它跟color是两套独立的绘制逻辑所以你可以做出白色填充 蓝色描边这种效果也可以用color: transparent做出纯空心字。我在实际项目里养成了一个习惯动手写任何文字特效之前先在纸上写清楚这个效果属于哪一层。属于轮廓层的就是描边类和部分阴影类属于绘制层的是渐变、纹理、颜色动画跨层的是 3D 挤压和故障风这种需要多个伪元素叠加的。分类清楚了改参数的时候就知道该动color、动-webkit-text-stroke还是动background不会再靠试。1.2 渐变字为什么必须同时写两个属性凡是做过渐变文字的人应该都写过这段.gradient-text { background-image: linear-gradient(90deg, #22d3ee, #a855f7); -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-text-fill-color: transparent; }background-clip: text让背景只保留文字覆盖的区域但这里有个容易被忽略的细节color: transparent和-webkit-text-fill-color: transparent是两个不同的属性后者优先级更高。只要写了-webkit-text-fill-color前面那个color就形同虚设。反过来说如果只写color: transparent在某些老版本的 WebKit 内核里文字仍然会被默认填充色盖住渐变就看不见了。所以稳妥的写法是两个都上。这不属于多写无害的保险而是确确实实有内核差异。我在做企业官网的年份数字时就因为图省事只写了color在客户的一台老 iPad 上显示成了纯黑实心字现场演示翻车的滋味不好受。注意background-clip: text现在依然是带前缀才最稳的状态标准写法要写-webkit-前缀也要写两个都写不冲突。1.3 四十八个特效的真实分类地图把四十八个效果按底层技法归类大概是这样一张表。你可以把它当成查手册用——看到某个想要的效果先在这里找到它属于哪一类再去对应的章节看实现逻辑。编号效果名称核心技法落地建议1空心字-webkit-text-stroke 透明填充大字号标题小字慎用2外描边加粗字双层伪元素偏移叠压需要粗描边时首选3内描边字text-shadow四向微偏移字号小于 24px 会糊4描边加渐变描边与background-clip组合海报类大标题5描边加发光描边 多层text-shadow暗底页面的强调标题6双层错位描边两个伪元素反向位移潮牌风、活动页7断裂描边字描边 clip-path切片只适合单向展示8虚线描边SVG 路径 stroke-dasharray需要引入 SVG9线性渐变字linear-gradient 裁切通用度最高10角度渐变字conic-gradient 裁切圆形徽标文字11彩虹渐变字多色linear-gradient儿童、活动主题12动态流动渐变字渐变 background-position动画停留时间长的标题13图片填充字background-image: url() 裁切品牌图、纹理图14金属拉丝字硬色阶渐变 斜向裁切需要调渐变断点15磨砂玻璃字半透明填充 backdrop-filter配深色背景16纸张纹理字噪点图 混合模式静态图更划算17单层投影字text-shadow两参数最基础18多层柔和阴影多组低透明度阴影阴影别超过四层19长投影字循环生成多组偏移用预处理器生成203D 挤压字多组同向硬阴影组数决定厚度21浮雕内阴影字上下双向text-shadow浅色背景更明显22霓虹光晕字多层同色text-shadow暗底效果最佳23双色错位字伪元素红蓝位移模仿色差24悬浮立体字阴影位移 transform需要 hover 配合25打字机效果widthsteps()动画中文需换算宽度26光标闪烁border-right动画和打字机配套27逐字入场拆字 animation-delay建议交给 JS 拆28故障风clip-path切片 色差动画别常驻播放29霓虹呼吸阴影透明度循环注意移动端耗电30波浪位移transform 逐字延迟一个字符一个元素31渐变旋转conic-gradient 角度变量需要注册自定义属性32抖动效果短时高频位移频率别超过 10Hz33闪烁效果opacity阶跃动画用于警示类文案34翻牌效果rotateX 透视需要三维上下文35打字加删除打字与退格循环双段动画串联36下划线伸展background-size过渡导航链接首选37边框描边跟随伪元素边框 过渡按钮类文字38光泽扫过斜向渐变条位移只用transform39涟漪扩散径向渐变 坐标变量坐标需 JS 传入40遮罩揭示mask 过渡注意内核差异41字符打散transform 随机延迟元素数量要控制42混合模式叠加mix-blend-mode背景必须不透明43字体粗细变化font-variation-settings需要可变字体44随机乱码字符替换 定时器纯 CSS 做不了45竖排文字writing-mode标点符号要处理46镜像倒影transform: scaleY(-1) 遮罩需要额外元素47删除线动画text-decoration或伪元素动画只能用伪元素48首字下沉::first-letter只对块级元素生效这张表本身就是一份提纲。接下来的几个章节我挑出其中最容易出问题、也最容易被用错的几类展开说。2. 描边与渐变这两组地基效果参数差一点观感差一截2.1-webkit-text-stroke的能力边界先说结论-webkit-text-stroke是从字形轮廓向两侧同时扩张的。也就是说你写2px文字最终会比原字形向外多出 1px、向内也多占 1px。这个特性带来两个非常实际的后果。第一描边会吃掉字形的内部空间。字号小的时候一条 2px 的描边足以把口日回这类字的内腔完全堵死字变成一坨。我的经验是描边宽度不要超过字号的 1/20。48px 的字描边最多 2.4px16px 的正文里想用描边基本只能做到 0.8px 到 1px再宽就没法看了。第二描边不会跟着字体粗细自动调整。同一段代码换成不同字重的字体观感可能完全不同。细体字配粗描边会显得像铁线框粗体字配细描边则几乎看不出来。所以做描边字的时候我一律会把字重锁死在700或900配合font-family里优先选思源黑体、苹方这类笔画均匀的无衬线字体。.stroke-heavy { font-family: Source Han Sans SC, PingFang SC, system-ui, sans-serif; font-weight: 900; font-size: 64px; color: transparent; -webkit-text-stroke: 2px #ffffff; }2.2 想要粗描边用伪元素叠压别硬拉大描边数值如果设计稿要求描边占字宽的三分之一这种夸张效果靠调大-webkit-text-stroke是死路笔画交叉处会糊成一团。正确做法是用伪元素复制一份文字放到主文字下层然后向各个方向做轻微位移。.outline-thick { position: relative; display: inline-block; font-weight: 900; color: #0f172a; z-index: 1; } .outline-thick::before { content: attr(data-text); position: absolute; inset: 0; z-index: -1; color: #38bdf8; -webkit-text-stroke: 6px #38bdf8; transform: translate(0, 0); }content: attr(data-text)是关键这样 HTML 里写一次文字伪元素自动同步改文案不用改两处。用inset: 0让伪元素和主文字完全对齐再通过-webkit-text-stroke撑出厚度主文字压在上面就能得到干净的粗描边。提示伪元素方案必须给外层设position: relative否则伪元素会以最近的定位祖先为参照一旦页面结构复杂就会跑偏。2.3 双层错位描边为什么好看四十八个效果里被问得最多的是那种文字有两个彩色轮廓一前一后的效果。它的原理特别简单就是两个伪元素反向位移一个往左上一个往右下。.chroma-text { position: relative; display: inline-block; color: #f8fafc; z-index: 1; } .chroma-text::before, .chroma-text::after { content: attr(data-text); position: absolute; inset: 0; z-index: -1; } .chroma-text::before { color: #22d3ee; transform: translate(4px, 4px); } .chroma-text::after { color: #f472b6; transform: translate(-4px, -4px); }位移量我一般取字号的 5% 到 8%。太小看不出层次太大就散了。另外两个伪元素的颜色要好选青加洋红、橙加蓝这种互补组合最稳别用两个相邻色相糊在一起像印刷套印不准。2.4 渐变字的兜底方案渐变字最大的风险是在某些环境下变成黑块或者全透明什么都看不见。所以生产环境里我一定会加一层兜底外层给一个纯色color把渐变效果放在支持background-clip: text的环境里。.gradient-safe { color: #a855f7; } supports (background-clip: text) or (-webkit-background-clip: text) { .gradient-safe { background-image: linear-gradient(90deg, #22d3ee, #a855f7, #22d3ee); background-size: 200% 100%; background-clip: text; -webkit-background-clip: text; color: transparent; -webkit-text-fill-color: transparent; animation: grad-move 5s linear infinite; } } keyframes grad-move { to { background-position: -200% 0; } }注意渐变的首尾颜色我特意都写成#22d3ee这是为了让循环无缝。如果你写的是青到紫的渐变然后让它动动画回到起点的那一瞬间会有明显的跳变因为渐变的起点色和终点色不一样。这个细节很少有人提但实际做动态渐变的时候是绕不过去的。3. 阴影叠加那一套从纸片字到立体字只差一个循环3.1text-shadow的本质是复制字形不是打光很多人潜意识里把text-shadow当成给文字打光觉得偏移越大越像有光源。实际上浏览器做的事是把字形轮廓复制一份、偏移一点、填上指定颜色、贴在主文字下层。理解这一点很多现象就解释得通了。比如阴影会和文字重叠——因为它是完整复制不是只画边缘。所以当偏移量小于描边宽度的时候阴影会被主文字盖住大半你就看不见效果。再比如多层阴影叠加会越叠越暗——因为每一层都是实心复制的透明度相同的两层叠起来就比一层更浓。基于这个认知我调阴影参数的顺序固定是先确定偏移方向和距离再确定模糊半径最后定颜色和透明度。顺序反过来的话你会一直在改颜色试图掩盖偏移不对的问题。3.2 3D 挤压字一个循环搞定厚度那个看起来很有厚度的 3D 字本质是把同一组阴影参数沿着一个方向重复三十次每次偏移递增 1px。.extrude { color: #fde047; text-shadow: 1px 1px 0 #a16207, 2px 2px 0 #a16207, 3px 3px 0 #a16207, 4px 4px 0 #a16207, 5px 5px 0 #a16207, 6px 6px 0 #a16207, 7px 7px 0 #a16207, 8px 8px 0 #a16207, 9px 9px 0 #a16207, 10px 10px 0 #a16207; }手写十行还能忍要写三十行就得靠预处理器或者构建时的循环。我用 Sass 的for生成输出到 CSS 里就是上面这种展开形式。这里有个性能问题值得提醒每一层阴影都是独立绘制的三十层阴影意味着这个文字元素要画三十一遍。单个标题没问题要是用在列表里一百条数据上滚动就开始掉帧了。注意3D 挤压字建议控制在十五层以内。视觉上十五层和三十层差别很小但渲染开销差一倍。3.3 长投影的角度一致性长投影是我个人最喜欢的一类文字特效因为它看起来设计感很强但实现成本很低。做法和 3D 挤压几乎一样区别在于挤压字每一层的偏移量是 1px 递增的等差数列长投影则要保证每一层落在同一条射线上。假设投影方向是四十五度那第 n 层应该是(n, n)如果方向是三十度就得算cos30和sin30的比例。我一般偷懒直接用像素坐标写明确的偏移序列比如(1,0.6) (2,1.2) (3,1.8)这样比按键值算三角函数快得多视觉效果没差别。3.4 内阴影字为什么在浅色背景上更明显所谓内阴影字是用双向text-shadow模拟出字是刻进去的感觉.emboss { color: #cbd5e1; text-shadow: -1px -1px 0 rgba(255, 255, 255, 0.9), 1px 1px 0 rgba(15, 23, 42, 0.35); }这里的第一层阴影方向朝向光源第二层朝向背离光源对比度决定了雕刻的深浅感。关键点是主文字的填充色必须介于两层阴影色之间否则对比关系就断了。我试过把主文字写成纯白结果整个字看起来像浮在纸张上面完全不是雕刻感。用#cbd5e1这类中间灰效果最接近真实。4. 动效类特效打字机、故障风、霓虹呼吸的真实坑在哪4.1 打字机效果的宽度问题中文场景下ch单位不能用网上流传的打字机代码基本长这样.typewriter { display: inline-block; white-space: nowrap; overflow: hidden; border-right: 3px solid currentColor; animation: typing 3.2s steps(14, end) forwards, caret 0.7s step-end infinite; } keyframes typing { to { width: 14ch; } } keyframes caret { 50% { border-color: transparent; } }英文场景没问题因为1ch恰好约等于一个字符宽度。但中文场景就崩了1ch是一个数字0的宽度一个汉字大概是2ch。你写14ch只够显示七个汉字。而且steps(14)的分段数也对不上——汉字是被半截切掉的因为一次步进只移动半个汉字宽度。我的处理办法有两个。一是把宽度单位换成em一个汉字约等于1em写width: 7em分段数写成steps(7)。二是在文案会变的情况下用 JS 测量实际宽度然后写入 CSS 变量这样最准但要接受多一层脚本依赖。样式表里写死的方案只适合固定文案。4.2 故障风的层次结构少了任何一层都不像故障风看着复杂拆开就是四层底层是主文字上面两个伪元素分别裁切出不同的横条并做左右微移颜色一青一洋红。裁切靠clip-path的inset()位移靠transform。.glitch { position: relative; } .glitch::before, .glitch::after { content: attr(data-text); position: absolute; inset: 0; } .glitch::before { color: #22d3ee; animation: glitch-a 2.4s steps(2, end) infinite; } .glitch::after { color: #f472b6; animation: glitch-b 2.4s steps(2, end) infinite; } keyframes glitch-a { 0%, 100% { clip-path: inset(0 0 62% 0); transform: translate(-3px, 0); } 50% { clip-path: inset(72% 0 0 0); transform: translate(3px, 0); } } keyframes glitch-b { 0%, 100% { clip-path: inset(58% 0 0 0); transform: translate(3px, 0); } 50% { clip-path: inset(0 0 78% 0); transform: translate(-3px, 0); } }两个inset()的裁切区域要错开否则两条彩带叠在一起看起来只是一条。位移量控制在 2px 到 4px超过 5px 就变成重影而不是故障。另外steps(2, end)是刻意的故障感来源于跳跃而不是平滑过渡用ease反而变味。4.3 霓虹呼吸动画成本要提前算霓虹字的核心是多层同色text-shadow从内到外模糊半径递增.neon { color: #f0abfc; text-shadow: 0 0 4px #f0abfc, 0 0 12px #e879f9, 0 0 24px #c026d3, 0 0 48px #a21caf; }配合呼吸动画的时候很多人会去动text-shadow的模糊半径这是最差的做法——模糊半径变化会触发重新绘制整个阴影图层四层阴影每帧重画移动端直接热起来。正确做法是只动opacity让整个元素做透明度循环浏览器只需要合成不走重绘。视觉上几乎看不出区别性能差好几倍。提示这一类看起来在变但实际没变形状的动画优先用opacity和transform这是能拿到合成层加速的两个属性。4.4 逐字入场和波浪位移元素数量必须控住逐字入场和波浪位移都需要把一个句子拆成一个个字符每个字符包一个span然后靠animation-delay做错峰。这套东西 CSS 做不了拆分必须靠 JS 或者手写。我的经验值是这样的二十个字符以内随便拆五十个字符以上就要谨慎。因为每个字符都是一个独立的 DOM 元素加一个独立的动画实例字符数上去以后初始化开销和内存占用都会明显增长。如果非要对整段话做波浪更聪明的做法是拆成词而不是字元素数量能降到四分之一。另外animation-delay一定要用calc()配合索引变量来算比如--i传进去animation-delay: calc(var(--i) * 60ms)。直接在 HTML 上写内联样式虽然也能跑但样式和结构混在一起后面改文案的时候就得手动重排序号很容易错。5. 交互类文字特效hover 这一下怎么做得不像模板5.1 方向感知的 hover靠 CSS 变量传坐标普通的 hover 展开效果是从下往上或者从中心放大看起来很套路。方向感知的做法是鼠标从左边进动画从左边开始从右边进从右边开始。纯 CSS 判断不了进入方向需要 JS 把鼠标相对元素的位置写进 CSS 变量。.direction-hover { --mx: 0.5; --my: 0.5; position: relative; overflow: hidden; } .direction-hover::after { content: ; position: absolute; inset: 0; background: #0ea5e9; transform: scaleX(0); transform-origin: var(--mx) var(--my); transition: transform 0.45s ease; } .direction-hover:hover::after { transform: scaleX(1); }JS 只需在pointermove里把offsetX / offsetWidth赋给--mx就行。注意transform-origin只在动画开始的那一瞬间被读取所以即使鼠标在动画过程中移动也不会造成抖动这一点比我预想的稳。5.2 光泽扫过为什么我用transform而不是left光泽扫过是个特别提气的效果一条斜向白色渐变带从文字上一扫而过。很多教程会写left: -120%到left: 140%做过渡能跑但每次变化都会触发布局计算。我改成用translateX从-120%到220%配合overflow: hidden.shine { position: relative; overflow: hidden; } .shine::after { content: ; position: absolute; top: 0; left: 0; width: 55%; height: 100%; background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.75), transparent); transform: translateX(-140%) skewX(-20deg); transition: transform 0.65s ease; } .shine:hover::after { transform: translateX(260%) skewX(-20deg); }skewX(-20deg)是让光带倾斜跟文字形成角度看起来才有金属反光感。角度我试过 -15 到 -30 度最后留在 -20因为太平了像水印太斜了像划痕。5.3 涟漪扩散和坐标变量涟漪的本质是一个径向渐变圆心位置由鼠标点击或移入的坐标决定.ripple { position: relative; overflow: hidden; isolation: isolate; } .ripple::before { content: ; position: absolute; inset: 0; background: radial-gradient(circle at var(--rx, 50%) var(--ry, 50%), rgba(255, 255, 255, 0.65), transparent 62%); opacity: 0; transition: opacity 0.4s ease; } .ripple:hover::before { opacity: 1; }这里的isolation: isolate是我后来补上的。原因是我遇到过一次诡异的现象涟漪的白色渐变叠在文字上以后文字颜色被洗淡了因为浏览器把这个伪元素和背景做了混合。加isolation强制建立独立层叠上下文问题就没了。这个坑在渐变配mix-blend-mode的场景里尤其容易出现。5.4 遮罩揭示在真实项目里的兼容处理遮罩揭示是做文字从图片缝隙里露出来的效果靠mask-image加渐变配合位移。它的兼容性坑主要集中在两点一是部分内核需要-webkit-mask-前缀且两套语法不能混用二是遮罩的合成在某些设备上会出现边缘锯齿。我的做法是给遮罩加一个极小的模糊过渡或者直接把遮罩渐变的过渡区间从 0% 到 100% 拓宽到 10% 到 90%边缘的台阶感就消失了。这个技巧不算优雅但是很有效。问题现象大概率原因处理方式文字完全看不见填充色和背景色相同或裁切失败临时把color改成亮色排查渐变只显示一小段元素是行内元素背景铺开范围不对改display: inline-block描边把字糊死描边宽度超过字号 5%降描边或换伪元素方案动画掉帧动画属性触发了重绘换成transform/opacity伪元素位置偏移外层没有定位上下文外层加position: relativehover 动画抖动过渡属性包含会触发布局的值只保留可合成的属性6. 四十八个效果里哪些能上生产、哪些只能做演示6.1 一条简单的判断标准动画属性跑在哪个线程我把这四十八个效果分三档。第一档是随便用包括描边、渐变、静态阴影、删除线、首字下沉这类没有动画或者只动opacity、transform的效果它们对性能的影响可以忽略。第二档是用的时候看一眼场景包括多层阴影的挤压字、霓虹呼吸、光泽扫过单个元素没问题成规模使用就要谨慎。第三档是只适合做展示页包括逐字入场、故障风、波浪位移、随机乱码这些效果的元素数量和动画实例数量都容易失控。判断的关键不在于效果本身好不好看而在于它触发了布局、重绘还是合成。动width、left、margin会触发布局动background-position、text-shadow、filter会触发重绘只有动transform和opacity才可能只在合成层处理。第二档和第三档的效果只要把动画属性从前者换成后者往往就能救回来。6.2 移动端和低端设备的降级策略我现在的习惯是给所有动效类文字特效都套一层媒体查询在窄屏下把动画直接关掉media (max-width: 768px) { .glitch::before, .glitch::after, .wave-char { animation: none !important; } }这不是偷懒而是很实在的取舍手机上用户滑得很快根本看不到你的逐字动画播完但处理器和电池是实打实在消耗。把动效留给大屏是一个性价比很高的决策。6.3 别忽略动效的减弱设置系统层面有个减少动态效果的开关很多用户出于舒适度考虑会打开它。CSS 里有对应的媒体查询media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } }把这些效果在这套查询里统一降级其实不只是照顾特殊人群也能顺带解决一部分低端设备的问题。我在一个内容站点上加了这段之后客服反馈里页面看起来在闪的投诉直接消失了。6.4 字体缺失导致特效错位的排查链路有段时间我遇到一个很费解的现象本地调好的描边字发到测试环境后描边位置整体偏了一点点看起来像没对齐。排查了大半天过程是这样的第一步先怀疑代码把样式表完整对比了一遍没差异。第二步怀疑浏览器版本用同一台机器换内核测试现象依旧。第三步把两边的网络请求抓出来对比发现字体文件不一样——本地命中了系统里的思源黑体测试环境没装回退到了默认的无衬线字体。不同字体的字形轮廓宽度不同描边的扩张基准点也就不同看起来就是偏移。解决方式很直接把字体文件放进项目里自托管或者至少在字体栈里明确写上两三个备选并接受备选字体下的观感差异。从那以后我养成一个习惯做任何依赖字形轮廓的特效之前先确认字体是不是受控的。这个坑不算深但不走一遍排查流程很难想到是字体的问题。7. 把散落的效果整理成能复用的样式体系7.1 用自定义属性把可调参数抽出来四十八个效果如果每个都写死数值改一次主题色就要全局搜索替换。我的做法是每个效果只暴露几个关键变量.text-effect { --fx-stroke: 2px; --fx-stroke-color: #ffffff; --fx-offset: 4px; --fx-color-a: #22d3ee; --fx-color-b: #f472b6; -webkit-text-stroke: var(--fx-stroke) var(--fx-stroke-color); }这样做的好处不只是好改更重要的是同一套代码可以做主题切换。把变量挂到根元素上整个页面的文字特效风格就跟着变了不需要重写任何一个效果的实现。我在一个多品牌复用的活动页模板里就是这么做的换品牌只改变量文件。7.2 命名和目录怎么组织才不乱我最后把这些效果整理成三个目录text-fill填充类渐变、纹理、图片填充、text-outline轮廓类描边、错位、多层叠压、text-motion动效类入场、悬停、循环。类名前缀统一用fx-后面跟效果名比如fx-gradient、fx-glitch、fx-shine。这样组织之后有个意外的好处排查问题的时候能快速定位。用户反馈某个标题的动画不生效你一看类名是fx-开头同时引用了两个不同目录的样式立刻能猜到是多个效果叠加导致的属性冲突而不是从头查起。7.3 从复制粘贴到真正能复用收藏四十八个代码片段和拥有一套可复用的样式体系中间隔着的不是技术是取舍。每个效果都要问自己三个问题这个效果在我的页面里有没有使用场景它在最差的设备上表现如何改动主题色的时候它会不会成为负担三个问题里有两个答不上来这个效果就不该进样式库。我自己最后留下来常用的大概只有十二个剩下三十多个属于知道怎么做、需要时能立刻写出来的状态。这个比例我觉得挺合理毕竟收藏的目的从来不是全部用上而是在需要的那一刻不用临时去搜。最后分享两个小技巧。一个是做任何多层伪元素叠压的文字效果时先把所有伪元素的color设成一个刺眼的亮绿色把对齐关系确认清楚再换成正式配色——这比对着半透明效果猜位置快得多。另一个是调阴影类效果时把页面的缩放临时调到 200% 以上再看很多在小尺寸下看不出来的错位、污渍和边缘锯齿会立刻暴露出来等到上线再发现就来不及改了。