CSS-Tricks,版本 20
过去几周我一直在猛搞这个网站的设计更新。是时候给界面换层新漆了,对吧?CSS-Tricks 的上一个版本发布于五年前。这在网页(以及狗的年龄)尺度上已经算远古了。
并不是说这个网站这些年一直原地不动。我们推送了大量改动,也上线了新功能,尤其是 2024 年以来,但我们确实一直没把画笔从储藏室里拿出来。
现在,在出现一片“看起来没那——么不同”的合唱之前,我想说,我很少喜欢彻底重建,更倾向于迭代式发布。而新的 v20 主题,我会称之为一次值得提升版本号的迭代式汇总。
我们来看看最值得注意的更新,看看为什么。
挖掘幽灵
首先,最大的变化中有些是你看不到的。可以想象,这个网
文章标题:CSS-Tricks,第 20 版
正文: 之前的主题有一个相当不错的“扁平”结构——分部目录很少,大多数东西都放在同一个顶层源码目录里。挺酷的。但像我这样有整理癖的人(你真该看看我家),必须得让每样东西都有个归宿。它并不完美,但我对这次在不把整个站点拆掉的前提下所能拼凑出的成果感到满意。

其中有一些妥协。比如,/header 其实应该放在 /layout 里。不过顺序确实很重要,而且我的一些理想方案根本行不通。我希望最终能引入某种 Cascade Layers 结构。
看到 CSS 的总体积在这个过程中下降,我高兴极了。它从 180 KB(31.7 KB)降到了 169 KB(24.6 KB)。算不上特别显著,但也绝非无足轻重。为了走到这一步,我不得不做了几个取巧的处理,比如把我接下来要讲的所有表单样式单独放进一个 forms.css 文件,并且只在加载了 WooCommerce、Restrict Content Pro 和 Gravity Forms 的页面上提供它。
一套勉强算得上的设计系统
你开始注意到某种趋势了吗?我们聊的仍然是相当不显眼的工作。CSS-Tricks 的角角落落太多了,几乎不可能让所有地方都完全一致。但正因为这些角角落落,许多固定值、魔法数字、一次性样式和较旧的工具类一直藏在各处。我决定在重新整理 Sass 的过程中处理掉其中相当大的一部分。
例如,现在有了语义化的设计 token,把一次性颜色抽象了出来。各种灰,我跟你讲。现在有了一套可维护的调色板,并在整个项目中引用。字体、字体样式和字号也是如此。内边距和外边距同样如此。在“系统”层面做一处改动,并且确信这处改动会在所有需要的地方生效,这种感觉好多了。
但这里也发生了一些清晰可见的变化!最值得注意的是,链接样式在全站范围内保持一致,只保留了少量有意维护的变体。这不仅在视觉上更讨喜,还提升了整体的可访问性,因为继承的样式默认提供了恰当的视觉提示,而不只依赖颜色或不透明度的变化。
哦,还有我最喜欢的部分?表单和表格都重做了。这在 WordPress 主题里很难做到,因为这些组件并不总是来自同一个来源。例如,表单可能来自 Gravity Forms、WooCommerce 或 Restrict Content Pro 插件中的任何一个,而它们各自都有自己的样式表。重置这些样式让我能在所有这些实例中带来一定程度的和谐。

主题切换
好,来点更直观的。这是 CSS-Tricks 第一次拥有不止一套配色方案!这事当然早就可以做,但我也想说的是,过去 8 年左右,设计其实并没有真正需要明暗切换。它是浅色和深色元素的巧妙融合,无论你的主题偏好如何,都能兼得两者的优点。
但我总得试一把,对吧?尽管内容一直放在白色背景上,网站整体一直以深色为主,所以我干脆把它当作“深色”模式已经存在来处理。结果就是一套奶油色的浅色主题,基本上只是背景和组件样式的变化。我还用上了从最近那场“切换”争论中学到的一些东西,用一个切换按钮来处理系统、深色和浅色主题。

没有改变的是你在文章等内容中看到的主内容容器。它本来就已经是白色,即使在“深色”模式下也是如此,而我不确定简单地在白色和黑色之间切换它是否是最好的做法。这肯定在考虑之中。
哦,而且这一切都接入了一个视图过渡,让新主题滑过上一个主题。
Jawbreaker 纹理
我希望你觉得网站的页眉和页脚很可爱。是的,可爱。这就是我在添加一串彩色圆点作为背景时所追求的效果。

有点像一种微妙的糖果纹理,对吧?或者是一群彩色的星星?我不知道,但因为想不出更好的说法,而且它让我想起小时候喜欢的 Jawbreaker 糖果,所以我就叫它“Jawbreaker”。命名仍然很烂。
它全都属于同一个 SVG。我真的、真的(真的)想用 random() 随机摆放圆点,但那会在标记里占用大量空的独立元素(例如 <i>)。不是我最喜欢的方式。所以,我用 PHP 生成这些 <circle> 元素:
<?php echo csstricks_jawbreaker_svg( array(
'count' => 90,
'seed' => mt_rand(),
'size_min' => 2,
'size_max' => 5,
) ); ?>
这是一大堆 SVG……

我真的、真的很想给所有东西都 random() 化,但把随机坐标内联到每个 <circle> 上才是可行之道。那些设置填充的内联样式、纹理中每个圆形的尺寸,以及为让它们闪烁出现和消失的细微动画设置 --delay 和 --dur 的变量,也是一样。
我得之后再研究用 <defs> 之类的方式生成 SVG,因为这种模式在页脚里也会重复。
这也延续到了 404 页面:

团块,团块,团块
如果你的浏览器支持 border-shape 和 random()(我认为只有 Chrome 支持),那么首页会在作者头像网格后面给你一个 blob 团块形状。悬停它,它就会变得,你知道的,更加 blob 化。
它采用了一个现有模式,我称之为 .gradient-card:
.gradient-card {
/* basic styles */
}
……并在组件的 :before 伪元素上设置渐变:
.gradient-card {
/* basic styles */
&:before {
content: "";
background: linear-gradient(
130deg,
#ff7a18 0%,
light-dark(#ff2d6b, #af002d) 41.07%,
light-dark(#00bfae, #319197) 76.05%
);
border-radius: 12px;
z-index: 1;
}
}
它位于半透明的 :after 伪元素下方,让 :before 的渐变透出一点点:
.gradient-card {
/* basic styles */
&:before {
/* etc. */
}
&:after {
content: "";
background: light-dark(color-mix(in srgb, white 72%, transparent), #000);
box-shadow: inset 0 2px 16px
light-dark(rgba(0, 0, 0, 0.12), rgba(0, 0, 0, 0.6));
border-radius: 8px;
z-index: 2;
}
不同的 border-radius 值让 :before 能向外溢出,营造出整体周围有一圈边框的效果。这就是基础形状。border-shape 才是让东西变得一团团的地方:
.gradient-card {
/* etc. */
&:before, &:after {
border: 1px solid transparent;
border-shape: var(--blob-shape-static);
clip-path: var(--blob-shape-static);
opacity: 0.5;
}
}
那些变量?它们映射到 shape()。第一个是我从 Temani Afif 的 blob 生成器里拿来的:
--blob-shape-static: shape(
from 50% 3%,
curve to 65.86% 11.71% with 59.41% 2.7%,
smooth to 72.37% 27.63%,
smooth to 77.43% 38.64%,
smooth to 88% 50%,
smooth to 92.79% 67.72%,
smooth to 81.37% 81.37%,
/* etc.
);
它被称为“static”,因为那是默认状态。悬停或聚焦该组件时,它会把这些点替换成该形状的“hover”版本:
--blob-shape-hover: shape(
from 50% 3%,
curve to var(--blob-point-1-x) var(--blob-point-1-y) with 59.41% 2.7%,
smooth to 72.37% 27.63%,
smooth to 77.43% 38.64%,
smooth to var(--blob-point-4-x) var(--blob-point-4-y),
smooth to 92.79% 67.72%,
smooth to 81.37% 81.37%,
smooth to var(--blob-point-7-x) var(--blob-point-7-y),
smooth to 50% 79%,
/* etc. */
);
那些变量呢?它们被映射到 random() 值:
--blob-point-1-x: random(62.86%, 68.86%);
--blob-point-1-y: random(8.71%, 14.71%);
--blob-point-4-x: random(85%, 91%);
--blob-point-4-y: random(47%, 53%);
--blob-point-7-x: random(60.22%, 66.22%);
--blob-point-7-y: random(78.93%, 84.93%);
/* etc. */
因此,每次页面加载时悬停状态都不一样。而且我们会在两者之间做动画:
&:hover,
&:focus-within {
&:before,
&:after {
border-shape: var(--blob-shape-hover);
clip-path: var(--blob-shape-hover);
@media (prefers-reduced-motion: no-preference) {
animation: scale-down 1.5s var(--ease-out-3) infinite alternate;
}
}
}
新版 Almanac 索引
这个我相当兴奋。过去几年,Almanac 不断成长、演进,变得非常出色。但它的主页面……就没那么出色了。它显然被内部页面的工作盖过了风头,除了通往各个功能子集的卡片链接之外,就再没有重新审视过。

现在这个页面是一个真正的 CSS 特性索引,能让你快速找到想去的地方。某时某地有人做过某种 CSS 元素周期表。我忘了在哪儿见过,但那个大致的想法留在了我脑子里,我觉得自己能做出来。它并没有什么“花招”,但我很喜欢这个布局为整个网站增添的东西。

新版指南索引
这是另一个急需关爱的区域。我们的指南一直是一列纵向堆叠的条目。那种书本效果确实很酷,但我开始认为,把它当作主要的视觉钩子会阻碍它变得更有功能性。大概有 27 篇指南,这意味着要滚动很久,而且你一路滚下去也不清楚会看到什么。

同样,没什么“花招”,但我喜欢新布局的整体实用性。有一个精选指南,一个展示新增和更新指南的小组件,下面则是一个分类网格,把所有内容都列出来。

作者页面
展示作者所有已发表文章的页面也稍微打理了一下。

其实只是个小改动。右侧边栏移到了左侧,作者头像和简介也移了进来,让文章能往上顶到顶部。我希望在这里做更多事情,因为这个小布局调整打开了更多可能性。
另外还需要处理更大的 Authors 索引,不过那是以后的事了。
WordPress 管理后台样式
这个只针对我们这些在 WordPress 编辑器里干活的人。我并不需要一个完整的可视化编辑器才能把事做完,但让一些前端样式混入编辑界面确实是个不错的便利,能让你更好地了解自己正在处理的内容。

v19 与 v20 对比
用 Lighthouse 来衡量性能、可访问性和最佳实践之类的东西,充满了各种需要留意的前提条件,但也没理由完全抛弃它。我觉得它们更多是好玩而非事实,我也不介意那些改进建议。
所以,经过几轮运行和调整,下面是主题与主题之间对比的结果。
Lighthouse 得分
| 类别 | v19 | v20 |
|---|---|---|
| 性能 | 71 | 72 |
| 无障碍 | 86 | 91 |
| 最佳实践 | 75 | 100 |
| SEO | 92 | 90 |
我对这个结果挺满意。性能方面有些地方我确实没法控制去修,所以看到 v20 和 v19 保持一致,我觉得就挺好。无障碍和最佳实践的提升让我更高兴。SEO?算了。除非它伤害到更重要的东西比如无障碍,不然我不太在乎。
Lighthouse 指标
| 指标 | v19 | v20 |
|---|---|---|
| LCP | 8.5s | 7.2s |
| CLS | 0.032 | 0 |
| TBT | 10ms | 13ms |
| FCP | 2.6s | 2.5s |
| Speed Index | 3.2s | 3.5s |
| Time to Interactive | 8.6s | 4.0s |
| 页面总重量 | 2.03 MB | 1.24 MB |
还是那句话,非常满意。首字节时间我几乎无能为力,但其他所有项目看起来至少都略有改善。页面总重量的下降是明显的胜利,这可能也是我在这里最在意的一点。
就这些?!
天哪,还有很多,但大多是不会浮到前端的琐碎改动。有点像浏览器更新日志里那些「修复若干 bug」之类的泛泛之言。在这里,是更好的文章查询、面包屑、函数管理、命名之类的……你懂的。
要做的事肯定还有很多。而且我确信一路上也漏掉或弄坏了不少东西。生活就是这样,那些只能留到下一次迭代版本里——那种不太值得升一个版本的发布。我哪里搞砸了,你可以在评论里告诉我。
希望至少你还喜欢它前进的方向!