你正在错误地使用 TailwindCSS

发布: (2025年12月17日 GMT+8 04:25)
13 min read
原文: Dev.to

抱歉,我没有看到需要翻译的正文内容。请提供您希望翻译成简体中文的文本,我会在保留原始链接和格式的前提下为您完成翻译。

我之前提到过,为什么我通常 推荐在我的项目中把 Tailwind CSS 作为主要的样式方案,我已经在这里详细解释了这种立场 here

这次,我想从更务实的角度谈谈 如何 正确使用 Tailwind CSS,而不是把它变成一种反模式。

网络样式简史

从前,有快乐的恐龙……好吧,这个时代太久远了。让我们回到现代 HTML 和 CSS 开始广泛可用的那个节点,大约在 HTML5 与 CSS3 稳定、网络开始快速扩张的时期。

同一时期,Twitter 推出了 Bootstrap,这是首批被广泛采用的基于组件的 CSS 库之一。Bootstrap 在当时堪称革命性:它提供了一套一致的视觉语言、合理的默认样式以及现成的组件,大幅降低了构建界面的工作量。

然而,Bootstrap 也伴随了一些权衡:

  • 它自带了非常主观的设计体系,这导致许多网站看起来几乎一样。
  • 虽然可以覆盖样式并使用主题,但样式冲突不可避免。
  • Bootstrap 需要固定的 HTML 结构;对任意元素的更改都可能导致整体样式失效。
  • 你还必须记住完整的结构或从文档中复制粘贴使用。

Source:

组件抽象的崛起

随着 ReactVue 等 JavaScript 框架的兴起,开发者体验(DX)显著提升。我们现在可以通过封装 HTML 标记和类,并通过 props/attributes 暴露干净的 API,达到更高层次的抽象。

抽象前(Bootstrap v5 模态框标记)

<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        Modal body text goes here.
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary">Save changes</button>
      </div>
    </div>
  </div>
</div>

抽象后(MUI v7.x 模态组件)

<Modal open={open} onClose={handleClose}>
  <Box sx={{ p: 2 }}>
    Modal body text goes here.
  </Box>
</Modal>

在这里,结构复杂性被隐藏在组件内部。使用者只需与清晰的 API 交互,而不必直接处理原始标记和脆弱的类结构。

实际上 Utility CSS 试图解决的问题

想象一个常见的情形:你需要在元素的行内起始位置添加一点点内边距,例如 5 px。于是你创建一个类 .padding-inline-start-5px。后来你需要 7 px,于是又添加 .padding-inline-start-7px。随着时间推移,这种做法会膨胀成数十甚至数百个微型类,其中不乏像 .padding-inline-start-7.8px 这样可疑的类。

Tailwind CSS 引入了一个真正好的概念:utility‑first CSS。它不是让你随意发明类名和数值,而是提供了一套 受约束且一致的比例,用于间距、颜色、排版等。该系统可以减轻决策疲劳,并在整个代码库中强制实现视觉一致性。

这部分在客观上是非常强大的。

那么为什么 Tailwind CSS 会受到抨击?

如果 Tailwind CSS 如此聪明,为什么许多开发者(包括我自己)经常对它持批评态度?

在我拙见中,问题 不在于 实用类(utility‑class)概念本身,而是 Tailwind 通常的引入和采用方式。它被呈现为一个拥有另一层抽象的框架,只提供 CSS 部分,未包含任何 HTML 或 JavaScript,并且你可以完全自由地按自己的方式使用它。

这种自由,加上它的快速流行,导致了困惑——尤其是在初学者中。很多人在真正理解 CSS 之前就开始学习 Tailwind CSS。像堆叠上下文、外边距塌陷或布局流等概念,如果从未学习过底层语言,就会显得神秘。

公平地说,这并非 Tailwind CSS 所独有。使用 React/Vue 而没有扎实的 JavaScript 基础的开发者也存在同样的模式。不过,Tailwind 无意中放大了这个问题,因为它让人可以在从未编写或深入理解 CSS 的情况下构建 UI。

我个人使用 Tailwind CSS 的方式

对我而言,Tailwind CSS 不是一种样式哲学;它是一个 实用工具库。我对待它的方式就像对待 JavaScript 中的 Lodash——它是一套有用的工具,能够简化常见任务,而不是语言本身的替代品。

这也是我欣赏像 UnoCSS 这样的项目的原因,它们在此理念上投入大量精力,并以更灵活的方向进一步推进,而不试图成为完整的独立框架。

如果我选择使用 Tailwind CSS,我的做法非常严格:

  1. 核心组件 使用普通 CSS 编写,并采用语义化的类名。
  2. Tailwind 的 CSS 变量 用于设计令牌(间距、颜色、排版)。
  3. 布局、动画、悬停状态以及复杂交互 放在 CSS 类中,而不是内联的实用工具链。
  4. 实用类 仅作为一次性微调的例外,而不是默认做法。

示例

Tailwind CSS 的问题用法

<!-- Example omitted in original source -->

上述将布局、定位、视觉样式和交互状态全部混在一个长长的实用工具链中,使得标记难以阅读和维护。

更务实的做法

<div class="card">
  <!-- content -->
</div>
/* card.css */
.card {
  @apply flex items-center justify-center;
  @apply absolute text-center bg-white rounded-xl shadow-md p-4;
  @apply hover:bg-gray-100 transition-colors duration-200;
}

这里组件的意图很明确(card),而 Tailwind 的 @apply 指令在专用的 CSS 文件中使用,使 HTML 保持简洁。

仅将 Tailwind 用作设计令牌

/* design-tokens.css */
:root {
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
  --spacing-lg: 2rem;

  --color-primary: #1d4ed8;
  --color-primary-hover: #1e40af;
}

/* Example usage */
.button {
  @apply bg-primary text-white py-[var(--spacing-sm)] px-[var(--spacing-md)];
  transition: background-color var(--duration-medium);
}
.button:hover {
  background-color: var(--color-primary-hover);
}

Tailwind 的配置提供了尺度,但实际样式写在 CSS 中,保持了关注点分离。

要点

  • 实用优先(Utility‑first)非常强大,但它应当是对语义化、可维护 CSS 的补充——而不是取代。
  • 将 Tailwind 视为工具箱,而非单一的整体框架。
  • 保持 核心组件标记的语义化,让 Tailwind 提供低层次的令牌(间距、颜色、排版)。
  • 内联实用链 仅用于快速、一次性的微调;否则应提取为可复用的 CSS 类。

按照这些指南操作,你即可享受 Tailwind CSS 带来的生产力提升,同时避免常被批评的陷阱。祝你样式愉快!

清理后的 Markdown

<div class="hello-tailwind">
  ## Hello Tailwind
</div>

为什么将布局、视觉设计和语义混合是有问题的

这种做法将布局、视觉设计和语义直接混入标记中。
现在想象一下,你需要在 5 个不同的地方 使用这个卡片,并且有轻微的差异。每次你都要复制并修改整段 class 字符串。当设计需求变化(而且总是会变化)时,你需要逐一查找所有实例并单独更新它们。

更简洁的替代方案:使用 Tailwind 的 CSS 变量

<div class="card">
  ## Hello World
</div>

<style>
.card {
  /* Layout and behavior */
  display: flex;
  position: absolute;
  text-align: center;

  /* Tailwind design tokens */
  background-color: rgb(var(--color-white));
  border-radius: calc(var(--radius) * 3);   /* rounded‑xl */
  box-shadow: var(--shadow-lg);
  padding: calc(var(--spacing) * 6);        /* p‑6 */
  width: calc(var(--spacing) * 80);         /* w‑80 */
}
</style>

添加修饰符

.card {
  /* …base styles… */
}

/* Modifier: primary */
.card.is-primary {
  background-color: rgb(var(--color-blue-500));
  color: rgb(var(--color-white));
}

/* Modifier: secondary */
.card.is-secondary {
  background-color: rgb(var(--color-gray-100));
  color: rgb(var(--color-gray-800));
}

注意: 这是真正的 CSS。嵌套现在已经是语言本身的一部分——无需预处理器。
CSS 仍在不断演进,其功能比许多人想象的更强大。详情请参阅 MDN。

合理使用 TailwindCSS 实用类

<div class="hello-world">
  ## Hello World
</div>

解决常见的反驳

在继续之前,让我们先回应一些常见的、支持纯粹实用主义(utility‑first)方法的论点:

  • “但是共置(colocation)让组件更易移植!”
    这确实如此,但前提是你永远不需要修改它们。一旦出现多个略有差异的实例,你要么复制整段 class 字符串,要么仍然需要创建包装组件。使用 Tailwind 变量的语义化类同样可以实现可移植性,并且可维护性更好。

  • “CSS 文件会变得臃肿且难以维护!”
    这在组件作用域 CSS 和 CSS 模块出现之前是对的。现代工具(CSS Modules、Vue scoped styles、CSS‑in‑JS、Svelte 等)彻底消除了这个问题。你的组件样式与组件本身一起存放。

  • “寻找未使用的 CSS 比未使用的实用类更困难!”
    使用组件作用域样式时,未使用的 CSS 会在删除组件时自动移除。使用 Tailwind 实用类时,PurgeCSS 能帮忙,但仍需注意动态类名的情况。

TailwindCSS 真正有意义的场景

在一种现代情境下,TailwindCSS 不仅合情合理,甚至可以说是最佳选择:即兴编码(vibe coding)。

当你使用 AI 工具进行原型设计时,主要目标往往是速度和视觉正确性,而不是长期可维护性。你关注的是屏幕上的效果,而不是 CSS 架构的优雅程度。在这种情况下,去设计语义化的类名、考虑抽象层次,或是仔细组织样式,都会感觉是多余的阻力。

实用优先(utility‑first)的 CSS 在这里表现良好,因为:

  • 反馈循环极其快速。
  • 样式紧贴 AI 正在生成或修改的标记。
  • 免去手动编写和调试 CSS 文件的工作。
  • 视觉微调既简单又可随意丢弃。

换句话说,TailwindCSS 与探索性、一次性思维方式高度契合。如果代码不需要长期存在、细致演进或由团队维护,那么为了速度而牺牲结构就是一种理性的权衡。

这也是 Tailwind 与 AI 工具天然匹配的原因。大型语言模型擅长生成实用类字符串,却在跨文件维护连贯、可演进的 CSS 架构方面表现较差。

最后思考

TailwindCSS 不是 错的。使用它作为 CSS 的替代品 错的。
当它被视为建立在扎实 CSS 知识和组件设计之上的实用层时,它可以非常高效。
当它被用作避免学习 CSS 基础的捷径时,它很快就会变成技术债务。

Back to Blog

相关文章

阅读更多 »

Tailwind 真的是慢吗?

Tailwind 成功俘获了众多开发者的心,同时也成为了许多其他人最讨厌的 CSS 框架。人们对它又爱又恨……