I Love Tailwind. Sorry, Not Sorry
昨天我写了一篇"我不喜欢 Tailwind"的文章,得到了不少反馈。今天我想写一篇"我喜欢 Tailwind"的文章,作为平衡。
不是因为昨天那篇文章错了。是因为那个观点太片面。
我用过 Tailwind,用过 CSS Modules,用过 Styled Components,用过 CSS-in-JS,用过普通 CSS。每个方案都有它的价值。今天这篇文章,是从"我喜欢 Tailwind"的角度,给出另一个角度的判断。
如果你昨天读了我的批评,今天请带着"这个人为什么喜欢 Tailwind"的心态读这篇。
我为什么喜欢 Tailwind
先说核心原因:Tailwind 让团队的前端开发效率提升了 3 倍。
这不是我拍脑袋的数字。这是我们团队的真实数据。
我们团队有 6 个后端开发者和 2 个前端开发者。以前做新功能,前端开发者是瓶颈——后端 API 半天就能写完,但前端页面要排 2-3 天。
用 Tailwind 之后,前端页面的开发时间从 3 天降到了 1 天。后端开发者也能参与简单的前端开发了,因为 Tailwind 不需要懂 CSS——只需要知道 p-4 是 padding,text-xl 是 font-size。
这不是 Tailwind 的功劳,这是降低前端开发门槛的功劳。Tailwind 把 CSS 的门槛从"需要学习选择器、优先级、盒模型、flexbox、grid"降到了"知道 p-4 是什么意思就行"。
这个门槛降低,让团队协作更高效。
Tailwind 的真实价值:设计约束
批评 Tailwind 的人会说:"它把样式和内容混在一起"。
我的反驳:设计约束比"语义清晰"更重要。
一个没有设计系统的团队,写出来的 CSS 是什么样的?
/* 各人写各人的,最后这个文件有 3000 行 */
.button-primary { background: #2563eb; }
.btn-submit { background: blue; }
.submit-btn { background: #0066FF; }
.primaryButton { background: #1E90FF; }
没有约束的 CSS,比 utility-first 更混乱。
Tailwind 提供的约束是:颜色只能是这些,间距只能是这些,字体大小只能是这些。这不是限制,这是设计系统的基础。
用 Tailwind 的团队,至少不会写出 10 种不同的蓝色来。
我用 Tailwind 解决的实际问题
问题一:快速原型迭代
我们有个内部工具,原来用 Bootstrap。Bootstrap 的问题是:所有的页面看起来都一样。
用户反馈:"你们的工具看起来像个 2015 年的网站。"
我们决定改版。用传统 CSS 改版,需要:设计稿 → 写 CSS → 测试。至少 2 周。
用 Tailwind 改版,我们直接在 HTML 上调样式,一边调一边给用户看。1 周完成改版,用户满意。
这不是"不专业",这是快速验证价值。
问题二:多人协作的样式冲突
我们团队有 6 个开发者,以前用普通 CSS,最大的问题是样式冲突。
开发者 A 在 buttons.css 里写了 .btn { border-radius: 4px; }。
开发者 B 在 forms.css 里写了 .btn { border-radius: 0; }。
开发者 C 在 components.css 里写了 .btn { border-radius: 8px; }。
最后按钮长什么样?取决于 CSS 文件的加载顺序。加载顺序变了,按钮的样子就变了。
用 Tailwind:
<button class="bg-blue-600 text-white px-4 py-2 rounded">
Submit
</button>
每个按钮的样式都在这个 class 里,没有跨文件的冲突。样式内聚,冲突消失。
问题三:响应式开发的效率
响应式开发用普通 CSS 是这样的:
.card {
display: flex;
flex-direction: column;
gap: 1rem;
}
@media (min-width: 768px) {
.card {
flex-direction: row;
}
}
HTML:
<div class="card">
<!-- content -->
</div>
这个写法没问题。但问题是:如果我想改移动端的布局,我要去 CSS 文件里找到 .card,然后改 media query。如果这个 .card 在 20 个页面里用,我得改 20 个地方?不,不对,改 CSS 文件一处就够了。
但现实是:很多团队的 CSS 不是一个 .card 到处用,而是一个 .card 在不同的文件里重复定义。
用 Tailwind:
<div class="flex flex-col gap-4 md:flex-row">
<!-- content -->
</div>
响应式断点直接写在 HTML 里,一目了然。不需要在 CSS 和 HTML 之间来回跳转。
Tailwind 让我惊讶的几个场景
场景一:ChatGPT 生成的 UI
去年我让 ChatGPT 帮我生成一个 Dashboard UI。它生成了这样的代码:
<button onclick="submitForm()" style="background-color: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer;">
Submit
</button>
行内样式,14 行。我要改成设计系统的样子,要花 20 分钟重构。
如果我用 Tailwind,可以让 ChatGPT 帮我生成 Tailwind 版本:
<button class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700">
Submit
</button>
这段代码我可以直接用,不需要重构。因为 Tailwind 的 class 是设计系统的一部分,颜色、间距、圆角都是预设的。
这就是AI + Tailwind 的组合价值:AI 生成的代码不用重构,直接可用。
场景二:维护别人的代码
我接手过一个老项目,Bootstrap 写的,5 万行 CSS。没有注释,没有文档,没有设计系统。
我需要改一个按钮的颜色。我怎么找这个按钮的样式?
- 用 Chrome DevTools 检查元素
- 找到 Applied Styles
- 一层层往上找选择器
- 有些是 Bootstrap 的,有些是自定义的,混在一起
- 找到
btn-primary定义的位置 - 改了它,可能影响其他用
btn-primary的按钮
用 Tailwind:
<button class="bg-blue-600 text-white px-4 py-2 rounded">
Submit
</button>
我直接改这个 class:bg-blue-600 → bg-indigo-600。影响范围是这个按钮,清晰可控。
场景三:Design Token 的应用
有人批评 Tailwind:"颜色选择太多,会导致不一致"。
这让我想到 Design Token 的概念。
Tailwind 的 bg-blue-600 看起来是硬编码值,但实际上它是 语义化的:blue-600 是品牌的蓝色,red-600 是错误色,green-600 是成功色。
如果你需要改品牌色,只需要改 Tailwind 配置:
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
brand: {
50: '#eff6ff',
100: '#dbeafe',
500: '#3b82f6', // 这是主品牌色
600: '#2563eb', // 改这里,所有 bg-brand-600 都变
700: '#1d4ed8',
}
}
}
}
}
改一处配置,整个应用的品牌色全变了。这比改 CSS 变量更方便,因为 Tailwind 提供了完整的语义化层级。
回答对 Tailwind 的批评
批评一:"HTML 变得臃肿"
我承认这一点。但我要说:这是开发时的权衡,不是用户的代价。
用户看到的是加载后的网页,不是一行行 HTML 代码。开发者读的是 HTML + Tailwind class,这是开发效率的交换。
如果 HTML 臃肿让你不舒服,用 Extract Component 模式:
// 提取成组件
function Button({ children, variant = 'primary' }) {
return (
<button className={clsx(
'px-4 py-2 rounded font-medium transition-colors',
variant === 'primary' && 'bg-blue-600 text-white hover:bg-blue-700',
variant === 'secondary' && 'bg-gray-200 text-gray-900 hover:bg-gray-300',
)}>
{children}
</button>
)
}
// 使用
<Button variant="primary">Submit</Button>
HTML 保持整洁,样式在组件里封装。这不是 Tailwind 的问题,这是组件设计的问题。
批评二:"HTML 和 CSS 混在一起"
这是设计哲学的问题。
传统的 Web 开发:HTML 是结构,CSS 是表现。
这个分离在 1998 年是对的,因为那时候 HTML 和 CSS 是不同的人写的:HTML 作者写内容,CSS 作者写样式。
今天的前端开发:一个人同时写 HTML 和 CSS,组件既是结构也是表现。
组件时代的开发,HTML + CSS 放在一起是自然的。Tailwind 只是把这一点做到了极致。
如果你坚持 HTML 和 CSS 分离,Vue SFC 或 React 的 CSS-in-JS 适合你。
如果你接受 HTML + CSS 放在一起,Tailwind 适合你。
这不是对错,是开发模式的选择。
批评三:"学 Tailwind 要学另一套东西"
这是真的。但我要说:Tailwind 学起来比 CSS 快。
CSS 的学习曲线:
1. 选择器(class、id、tag、组合)
2. 优先级(specificity、!important)
3. 盒模型(margin、padding、border、content)
4. Flexbox
5. Grid
6. 响应式(media query)
7. 动画(@keyframes)
8. 变量(CSS Custom Properties)
9. 预处理器(Sass/Less)
10. 后处理器(PostCSS)
加起来要学的东西很多,而且很多概念是相互关联的。
Tailwind 的学习曲线:
1. 基础语法:p-4 = padding: 1rem
2. 响应式前缀:md:p-4 = @media (min-width: 768px) { padding: 1rem }
3. 变体:hover:bg-blue-600
4. 组件模式:提取成组件
4 个概念,能解决 80% 的问题。
剩下的 20%(自定义样式、CSS 动画)可以学标准 CSS,不需要全学。
Tailwind 在大型项目里的实践
我们用 Tailwind 做了一个 SaaS 产品,2 年,代码量 10 万行。
怎么保持一致性
我们有 tailwind.config.js,定义了:
- 品牌色(brand-*)
- 间距系统(space-*)
- 阴影(shadow-card、shadow-modal)
- 圆角(radius-sm、radius-md、radius-lg)
开发者不允许直接用 Tailwind 的原始值(如 bg-blue-600),必须用我们的设计 token(如 bg-brand-600)。
这样既利用了 Tailwind 的便利性,又保证了设计一致性。
怎么组织代码
// components/ui/Button.tsx
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ variant = 'primary', size = 'md', className, children, ...props }, ref) => {
const baseClasses = 'inline-flex items-center justify-center font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2'
const variantClasses = {
primary: 'bg-brand-600 text-white hover:bg-brand-700 focus:ring-brand-500',
secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200 focus:ring-gray-500',
ghost: 'text-gray-700 hover:bg-gray-100 focus:ring-gray-500',
}
const sizeClasses = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-6 py-3 text-lg',
}
return (
<button
ref={ref}
className={clsx(baseClasses, variantClasses[variant], sizeClasses[size], className)}
{...props}
>
{children}
</button>
)
}
)
所有组件封装好了,用的人不需要知道 Tailwind 写了什么。用 <Button variant="primary"> 就够了。
怎么做 Code Review
Tailwind 的 code review 跟普通 CSS 不一样。
普通 CSS review 问:"这个样式放在哪个文件里?"
Tailwind review 问:"这个按钮的 variant 是 'primary' 还是 'secondary'?"
Review 的是组件使用,不是样式细节。样式细节在组件定义里,review 一次就够了。
我的结论
Tailwind 是一个工具,工具的价值取决于用它的人。
用 Tailwind 写出混乱代码的人,不用 Tailwind 也会写出混乱代码,只是混乱的方式不一样。
用 Tailwind 写出高质量 UI 的团队,是因为他们知道什么时候用 Tailwind,什么时候不用,什么时候需要封装组件。
Tailwind 适合我,因为它:
1. 降低了团队的前端开发门槛
2. 提供了设计约束,让我不用担心"10 种蓝色"的问题
3. 让 AI 生成的代码直接可用
4. 响应式开发效率高
Tailwind 不适合你,如果你:
1. 追求 HTML 的绝对语义化
2. 有复杂的 CSS 动画需求(Tailwind 的动画支持有限)
3. 团队里有人坚决不喜欢 Tailwind(强行推广会有反效果)
这不是对错,是适合不适合的问题。
我喜欢 Tailwind,就像我喜欢 Go 一样。它们都是"做减法"的设计哲学:去掉你不需要的,留下你真正需要的。
如果你用过 Tailwind 并且喜欢它,这很正常。如果你用过并且不喜欢,也很正常。观点不同,互相尊重。