在 2024 年如何编写 CSS

令人惊叹的功能:

  • 跨浏览器支持嵌套、:has()、容器查询等
  • 强大而快速的新CSS工具
  • 许多框架和编译器帮助优化CSS加载性能

设计约束

用户体验

当访问网站时,加载样式表会呈现出怎样的出色体验?

  • 样式表应尽可能快速加载(文件大小小)
  • 样式表在未更改时不应重新下载(适当的缓存头)
  • 页面内容应具有最小或无布局移位
  • 字体应尽可能快速加载并减少布局移位
    开发者体验
    我们的工具必须帮助我们创建更好的用户体验。开发者体验虽然重要,但不能在用户体验之前。

我们使用的样式工具的开发者体验如何帮助我们创建更好的用户体验?

  • 修剪未使用的样式,对CSS进行缩小和压缩以减小文件大小
  • 生成带哈希的文件名以实现安全、不可变的缓存²
  • 将CSS文件捆绑在一起以减少网络请求
  • 防止命名冲突以避免视觉回归

那么,如何帮助我们编写更易维护、愉悦的CSS呢?

  • 在删除相应的UI代码时轻松删除样式
  • 轻松遵循设计系统或一组主题
  • 编辑器支持TypeScript,自动完成和代码检查反馈
  • 在编辑器中接收工具反馈以防止错误(类型检查,代码检查)

2024年的CSS

从未如此轻松地编写出色的样式,无需任何额外的工具。

下面的示例使用许多最新的CSS特性,支持跨浏览器,而无需任何构建步骤。您可能不再需要使用Sass或Less!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
:root {
--main-bg-color: #f3f4f6;
--title-color: #262626;
--text-color: #525252;
--font-family: "Arial", sans-serif;
}

body {
margin: 0;
padding: 0;
background-color: var(--main-bg-color);
font-family: var(--font-family);
}

.blog-header,
.blog-footer {
text-align: center;
padding: 1rem;
background-color: var(--title-color);
color: white;
}

.blog-post {
container-type: inline-size;
margin: 1rem;
padding: 1rem;
background-color: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);

& .post-title {
color: var(--title-color);
margin: 0 0 1rem 0;
text-wrap: balance;
font-size: 1em;
}

& .post-content {
color: var(--text-color);
}
}

@container (min-inline-size: 500px) {
.blog-post {
padding: 1.5rem;

& .post-title {
font-size: 1.25em;
}
}
}

这是否意味着不再需要工具?对于一些人来说,是的。

构建步骤

为了满足上述设计约束,你可能需要一个构建步骤。

不太可能所有的用户都使用最新版本的浏览器。但更重要的是,总会有一些尚未在所有浏览器上支持的新语法,而你却想要使用。

你可以手动编写@supports规则来检查浏览器支持,但这只是解决了一些问题。与其将CSS优化留给人类,为什么不让机器来处理呢?

编译

编译器使以下工作流变得简单:

  • 自动删除任何未使用的样式,将文件捆绑在一起以减少网络请求,添加供应商前缀,并通过删除空格和注释来缩小输出
  • 自动生成唯一的文件名,允许框架设置缓存头,比如不可变,向浏览器发出内容永远不会更改的信号
  • 指定目标浏览器(browserslist)并通过语法降级将现代CSS特性编译为适用于这些浏览器的代码
    流式CSS
    你访问谷歌以预订机票。它无法预先计算你的意图,所以你会看到一个用于初始UI的搜索栏。你搜索“Flight SFO to NYC”,服务器会实时流入一个航班小部件供你选择日期。

谷歌不可能预先包含每一个可能的小部件。货币转换、计时器、实时体育比分,你想要的都有。这些小部件的UI和样式需要动态流入。

React(以及Next.js)现在支持这种模式,具有流式SSRCSS。在React模型中,你将UI定义为具有样式依赖的组件。我们如何安全地为小部件流入样式,而不影响页面上的任何内容呢?

样式需要被限定,或者说是原子的,这样如果它们在预期的DOM内容之前加载,它们就不会改变页面上已有元素的样式。

例如,CSS模块具有对导入它的组件进行样式规定的规则。Tailwind使用原子实用类,这些类被编译成单个样式表,在任何类被使用之前加载。StyleX也生成原子类。全局样式在流式加载时效果不好,除非在流的开始加载。

我的建议

CSS模块

CSS模块是对纯CSS的一种小而有影响的增强。

它们实现了我们期望的UX约束,以及大多数(但不是全部)DX约束。它们几乎在每个现代打包工具和框架中都可用。你可以复制/粘贴现有的CSS选择器,它们将在CSS模块中正常工作,无需任何更改。

它们不能生成原子样式。它们不支持使用许多主题(只支持CSS变量)。而且因为样式代码存在于你的TypeScript文件之外,所以你不会得到类型安全和自动完成。但对你来说,这些约束可能没问题。

支持CSS模块的Lightning CSSVite使用,很快还会被TailwindNext.js使用。像postcssautoprefixer这样的工具正在被更快、一体化的Rust工具链所取代。

Tailwind CSS

Tailwind使用编译器只生成使用的类。因此,尽管实用的CSS框架包含许多可能的类名,但只有使用的类(例如“font-bold text-2xl”)将包含在单个编译后的CSS文件中。

假设你只编写Tailwind代码,你的捆绑包永远不会比使用的所有Tailwind类的总和更大。你极有可能不会使用它们全部。这意味着生成的CSS文件的大小有一个固定的上限,然后进行缩小、压缩和缓存以获得最佳性能。

你不必只编写Tailwind样式。Tailwind类只是符合设计系统的常规CSS实用程序。你可以将Tailwind与CSS模块混合使用,例如。

Tailwind有一些与之配套的工具:

  • VSCode集成,包括自动完成、代码检查、语法高亮等
  • Prettier集成,用于自动排序类名

关于Tailwind最有争议的部分是其语法。有人喜欢,有人讨厌。我在构建了一些东西之后才意识到Tailwind的好处,所以如果你的初次反应是反感,我建议试一试。

StyleX

大多数CSS-in-JS库存在两个问题:

  • 性能:组件必须将在JS中编写的样式转换为在渲染时插入文档的CSS。这可能会产生很大的成本,这就是为什么库正在转向“零运行时”库(如StyleX)的原因。
  • 兼容性:许多现有的CSS-in-JS库已经支持React的流式服务器渲染,但仍然不兼容其他性能优化,比如将应用程序的某些部分移到React Server Components

为了解决这些问题,“零运行时”CSS-in-JS库如Vanilla ExtractPanda等已经被创建。

StyleX是最新的CSS-in-JS库,解决了这些问题以及更多。如果你想深入了解,我建议阅读“Thinking in StyleX”

这个例子是我第一次使用StyleX。虽然它在开源领域还比较新(生态系统也反映了这一点),但它并不是一种新的库。它提供所有Meta网站:FacebookInstagramWhatsAppThreads

不过,你仍然需要为事物起个名字 🫠 比如buttonWrapperContainer

结论

对我来说,CSS现在是…有趣的吗?我想是的。我很期待未来几年会带来什么。

你会选择其他东西吗?我有遗漏什么吗?告诉我。

  • ¹:更多内容:linear()缓动、subgrid、动态视口单位、颜色空间和@layer。

  • ²:由于文件名是唯一的,你可以设置不可变的缓存头,告诉浏览器内容永远不会更改。这允许浏览器永久缓存文件,对性能大有裨益。