译:对 Tailwind 4 的思考

原文:https://nmn.sh/blog/2024-11-30-thoughts-on-tailwind-4
作者:nmn.sh
译者:ChatGPT 4 Turbo

编者注:这篇文章详细分析了 Tailwind CSS 4.0 测试版的重大更新和潜在问题。作者认为:1) 迁移到 LightningCSS 和使用原生 CSS 层叠层是明智之举,2) CSS 变量的性能问题需要关注,3) 新增的后代选择器变体可能会破坏原子化 CSS 的优势,4) 默认使用 rem 单位不利于可访问性,建议改用 px 单位,5) 类名设计还有改进空间。虽然 Tailwind 4 带来了许多显著改进,但某些新特性可能会导致开发者重蹈覆辙,重新陷入 Tailwind 本来想要解决的问题。

Tailwind CSS v4.0 测试版刚刚发布,我想分享一些关于它的想法。具体来说,我有一些尚未在其他地方看到的担忧。

与其提供任何高层次的见解,不如让我逐一讨论 Tailwind 4 的各种变化。

声明:这些观点仅代表个人意见,不代表我的雇主。

迁移到 LightningCSS

迁移到 LightningCSS 是一个明智之举。JS 社区正大规模转向基于 Rust 的工具,使用 LightningCSS 将有助于保持 Tailwind 与各种工具的易集成性。性能提升虽然显著,但并不特别重要。Tailwind JIT 的速度已经足够快了。

LightningCSS 最大的好处是 统一 了 Tailwind、导入和语法降级。不需要手动配置 Autoprefixer 是一个重大进步。

CSS 优先的配置

关于 CSS 优先的配置,很多人都很兴奋。我认为这并不像其他人认为的那样完美无缺。它有利弊。最大的好处是在开始时少了一个样板文件。

缺点是大多数配置都是 Tailwind 特有的自定义语法,而且不再是类型安全的。希望工具能帮助弥补这个差距。

同时,Tailwind 继续通过允许你在 CSS 文件中导入来支持旧的 JS 配置。

@config "../../tailwind.config.js";

尽管有这些权衡,我认为从长远来看这将证明是一个好的改变。

CSS 主题变量

这里开始出现一些担忧。从架构角度来看,使用 真正的 CSS 变量显然是一个胜利。然而,在实践中,使用 CSS 变量可能会带来一些令人意外的性能陷阱。性能问题并不一致,当在 :root 上定义大量变量时,影响似乎很小。但是,重新赋值变量或在 :root 之外的元素上定义变量可能会产生显著的性能影响。

因此,只要大多数开发者不过度使用自定义作用域覆盖变量,情况应该还好。也可以配置 Tailwind 在 CSS 输出中内联变量,就像 Tailwind 3 及更早版本一样:

@theme inline {
  /* ... */
  --color-black: #000;
  --color-white: #fff;
  /* ... */
}

大多数开发者都是 全局 配置他们的主题,变量的作用域覆盖并不是常见用例。所以,我在想内联变量是否应该作为默认选项。

原生 CSS 层叠层

这是一个完全的成功,显而易见的胜利。我最大的惊讶是发现这之前竟然还不是这样。有些较旧的浏览器不支持层叠层,但这是一个简单的 polyfill 就能解决的问题。

简化的主题配置

以前需要配置的许多值现在都允许任意值,比如 grid-cols-73,不需要 [73]这是一个好的改变,减少了不必要的样板代码和摩擦。

其他明显的胜利

有太多无法一一提及,所以这里列出一些其他明显的改进:

  • 自动源代码检测
  • 内置 @import 支持
  • 内置 CSS 转译
  • 动态间距比例
  • P3 颜色
  • 容器查询支持
  • 3D 变换(终于

还有很多其他的… 如果你想了解 Tailwind 4 的所有新特性,他们有一些很棒的文档。我会在我的观点限制在我认为 有争议 的部分。

渐变

Tailwind 4 终于添加了径向和圆锥渐变的支持。虽然这是一个明显的改进,但我认为 Tailwind 从未很好地处理渐变。内联定义渐变是有问题的,使用 CSS 变量来做这件事也是有问题的。在 HTML 中直接使用 "from-indigo-500 via-blue-400 to-teal-300" 不是一个好主意。

Tailwind 应该提供一组预定义的漂亮渐变,可以开箱即用,并让开发者在配置文件中定义自己的渐变

不过,这不是一个新问题。一直都可以选择不使用这些工具类,而在配置文件中定义渐变。

新的 not-*in-* “变体”

Tailwind 通过新的 not-*in-* 变体添加了对 :not():hover * 的支持。

<button class="bg-indigo-600 hover:not-focus:bg-indigo-700">
  <!-- ... -->
</button>

<div class="opacity-50 in-focus:opacity-100">
  <!-- 当在聚焦元素内部时将变为 opacity:1; -->
</div>

这些是很好的改进,也是保持原子化样式保证的好方法。

inert-*nth-* 变体

这些变体 略微 有问题。它们让你可以在类名中使用 [inert]:nth-child() 选择器。这鼓励了原子化 CSS 一般而言,特别是 Tailwind 一直以来都不鼓励的不良实践。

至少这些变体不鼓励"远距离样式",所以虽然它们可能会导致 CSS 膨胀,但不会特别有害。

后代变体

虽然很多人在庆祝这种对原子化 CSS 保证的破坏,但我认为这可能是 Tailwind 有史以来最 有害 的添加。

用于目标直接子元素的 * 变体已经添加了,这已经够有问题的了:

<ul class="*:p-4">
  <li>One</li>
  <li>Two</li>
  <li>Three</li>
</ul>

这积极地鼓励了"远距离样式",但至少它是受限的,仅限于直接子元素。新的 ** 变体完全破坏了这一点:

<div class="**:data-avatar:rounded-full">
  <div>
    <img src="…" data-avatar />
    <!-- 这个元素将变成圆形 -->
  </div>
  <p>…</p>
</div>

其他人看到的是力量,我看到的是混乱。Tailwind 终于跨过了那条线,把基本上所有的 CSS 都加入到了类名中。这不再是原子化 CSS,开发者们会滥用这个特性,最终陷入 Tailwind 本来想要帮他们避免的所有糟糕问题

现在还来得及,这个 “特性” 应该在为时已晚之前回滚!

其他长期存在的问题

既然在谈论 Tailwind,让我也提出一些我长期以来的担忧。

类名可以更好

Tailwind 通常 在类名中有一个"前缀"来表示正在应用:

  • bg-red-500 表示 background-color
  • m-sm 表示 margin
  • pt-4 表示 padding-top
  • 等等

然而,有些类名中应用的 样式 并不那么明显:

  • flex 应用 display: flex;
  • grid 应用 display: grid;
  • text- 可以应用 color font-size

在最近的版本中,这种情况变得更加普遍,比如 growshrink 分别替代了 flex-growflex-shrink

虽然这些改变对简洁性有好处,但有时会增加混淆,并可能对 tailwind-merge 等工具的代码大小和运行时成本产生重大影响。

Tailwind 团队倾向于不鼓励使用 tailwind-merge,并期望所有样式都静态应用。然而,在我看来,这是一个不切实际的期望,因为几乎每个使用 Tailwind 的严肃项目,通常也会使用 tailwind-merge

我认为 Tailwind 应该让 tailwind-merge 这样的工具更容易存在并尽可能高效。作为副作用,这也可能使样式更容易阅读和使用,更加一致

rem 单位的泛滥使用

更改默认主题很容易,但 Tailwind 的默认主题对所有东西都使用 rem 单位。从 font-size 到间距,从尺寸到媒体查询。设计师 喜欢 这个,因为这让他们能保持以前的比例,但这实际上是一个可访问性的反模式。

在浏览网站时,用户可以通过两种方式控制大小:

  • 使用页面缩放
  • 更改默认字体大小

对间距和尺寸使用 rem 剥夺了用户更改默认字体大小的能力,使其变成了第二种缩放方式。这对用户不友好,迫使那些可能需要稍大文字才能舒适阅读的用户使用为较小屏幕设计的布局,降低了密度

这个问题的解决方案很简单。间距、尺寸和媒体查询的默认值都应该使用 px 单位,而 rem 应该专门用于 font-size

结论

虽然这篇文章似乎集中在负面方面,但这只是因为正面的东西太明显了,没什么好说的。我在这里确实有点吹毛求疵,但考虑到 Tailwind 的巨大流行度,我认为提供建设性的批评并帮助改进这个工具对整个社区都很重要。

在我这边,tw-to-stylex 已经升级到在后台使用 Tailwind 4,我很兴奋能让它变得更加强大和功能丰富。