跳到主要内容

Tailwind CSS

Tailwind CSS 在前端项目里的位置,既不是传统意义上的“组件库”,也不是单纯的类名集合。它更像一层设计系统底座:把颜色、间距、圆角、排版、响应式规则先收成统一语言,再决定上面接 HeroUI、Base UI、业务组件,还是完全手写。

如果项目准备长期维护,或者设计稿变化频繁,Tailwind 往往会成为很稳的起点。

先明确 Tailwind 在解决什么

Tailwind 解决的核心问题,不是“少写 CSS”这么简单,而是这几件事:

  • 让样式表达和组件结构放在同一个地方
  • 让 spacing、color、radius、shadow 这些设计 token 更容易统一
  • 让响应式、状态样式、深色模式这些高频场景写法更直接
  • 让组件拆分之后,样式不容易散成一地

它不负责:

  • 组件逻辑
  • 可访问性行为
  • 弹层、表单、菜单这种复杂交互

所以 Tailwind 常见的搭配方式是:

  • Tailwind + 自己的业务组件
  • Tailwind + Headless UI / Base UI / Radix
  • Tailwind + HeroUI / shadcn/ui 这类现成体系

什么时候值得优先选 Tailwind

适合

  • 设计系统刚准备建立
  • 业务页面很多,样式容易失控
  • React / Vue / Next / Vite 主线项目
  • 团队愿意接受 utility-first 写法
  • 中后台、内容站、营销页都要统一在一套 token 上

不太适合

  • 项目本身严重依赖 Sass mixin 和大段嵌套写法
  • 团队完全不接受类名堆叠的代码风格
  • 项目浏览器支持要求过旧

官方兼容性文档也明确提到,Tailwind CSS v4 面向现代浏览器,而且不以 Sass、Less、Stylus 这类预处理器为主要工作流。

Tailwind v4 的变化,先记住这几条

Tailwind v4 和很多人熟悉的 v3 时代已经不太一样了。

最值得先记住的是:

  • 安装链路更短
  • 配置更偏 CSS-first
  • @theme 成了设计 token 的主入口
  • 官方有了第一方 Vite 插件
  • 很多“以前得写配置文件”的事,现在更适合直接放在 CSS 里

如果项目还停留在 tailwind.config.js 是一切中心的理解上,切到 v4 时最好先把这层心智模型换掉。

在 Vite 项目里怎么接

这是 v4 最顺的接法之一。

安装依赖

pnpm add tailwindcss @tailwindcss/vite

配置 vite.config.ts

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
plugins: [react(), tailwindcss()],
})

在主样式文件里引入

@import "tailwindcss";

直接开始使用

export function App() {
return (
<main className="mx-auto max-w-5xl px-6 py-12">
<h1 className="text-4xl font-semibold tracking-tight">Hello Tailwind</h1>
<p className="mt-4 text-base text-zinc-600">先把 token 和布局习惯收稳,后面接组件库会轻松很多。</p>
</main>
)
}

在 Next.js 项目里怎么接

如果项目不是直接走 Vite,而是 Next.js、Angular 这类更依赖 PostCSS 的链路,官方更推荐 PostCSS 接法。

安装依赖

pnpm add tailwindcss @tailwindcss/postcss postcss

配置 postcss.config.mjs

export default {
plugins: {
'@tailwindcss/postcss': {},
},
}

app/globals.css 或主样式文件里引入

@import "tailwindcss";

App Router 下的最小结构

// app/layout.tsx
import './globals.css'

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="zh-CN">
<body className="bg-white text-zinc-900 antialiased">{children}</body>
</html>
)
}

一个更接近真实项目的起步配置

Tailwind v4 很多配置更适合直接写在 CSS 里。下面这份就足够支撑大部分前端项目起步。

@import "tailwindcss";

@theme {
--font-sans: "Inter", "PingFang SC", "Microsoft YaHei", sans-serif;

--color-brand-50: oklch(0.98 0.02 250);
--color-brand-100: oklch(0.94 0.04 250);
--color-brand-500: oklch(0.62 0.19 255);
--color-brand-600: oklch(0.55 0.18 255);

--radius-card: 1.25rem;
--shadow-soft: 0 10px 30px rgba(15, 23, 42, 0.08);

--breakpoint-3xl: 120rem;
}

@layer base {
html {
color-scheme: light;
}

body {
@apply bg-white text-zinc-900;
}
}

这一层做完之后,项目里的大部分样式决策都会开始统一。

常见的前端集成方式

1. 配合 React / Vue 组件直接写类名

function Card({ title, description }: { title: string; description: string }) {
return (
<article className="rounded-[var(--radius-card)] border border-zinc-200 bg-white p-6 shadow-[var(--shadow-soft)]">
<h2 className="text-lg font-semibold text-zinc-900">{title}</h2>
<p className="mt-2 text-sm leading-6 text-zinc-600">{description}</p>
</article>
)
}

2. 配合 clsxtailwind-merge

业务组件变体一多,这套组合会很常见。

pnpm add clsx tailwind-merge
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
import { cn } from '@/lib/cn'

function Button({ primary = false }: { primary?: boolean }) {
return (
<button
className={cn(
'inline-flex items-center rounded-full px-4 py-2 text-sm font-medium transition-colors',
primary
? 'bg-brand-500 text-white hover:bg-brand-600'
: 'bg-zinc-100 text-zinc-900 hover:bg-zinc-200'
)}
>
Button
</button>
)
}

这类写法很适合:

  • Button / Badge / Alert / Tabs 这类组件变体
  • 表单状态切换
  • 响应式类名拼接

3. 配合 Headless 组件库

如果交互复杂,又不想自己实现无障碍逻辑,Tailwind 很适合搭在 Headless 组件库上面:

  • Base UI
  • Radix UI
  • Headless UI

这一层负责:

  • 菜单逻辑
  • 焦点管理
  • 键盘导航
  • 弹层行为

Tailwind 负责视觉层和状态表达。

常用插件和扩展能力

Tailwind v4 已经把很多能力做进核心,但项目里仍然常会补几类插件。

官方常用插件

@tailwindcss/typography

适合:

  • 博客
  • 文档站
  • 富文本文章页

安装后最常见的收益,是不用手写一整套 Markdown 排版规则。

@import "tailwindcss";
@plugin "@tailwindcss/typography";
<article className="prose prose-zinc dark:prose-invert">
<h1>文章标题</h1>
<p>正文内容</p>
</article>

@tailwindcss/forms

适合:

  • 表单较多
  • 想先把原生表单控件拉齐
@import "tailwindcss";
@plugin "@tailwindcss/forms";

它不是完整表单组件库,更像把 input、select、textarea 的基础样式先收平。

社区里常见的扩展

tailwindcss-animate

适合:

  • 弹层进入退出
  • 手势反馈
  • 状态过渡

@iconify/tailwind

适合:

  • 想在 Tailwind 类名里直接使用图标选择器
  • 图标规模比较大

这些不是必须项,但在营销页、设计系统和中后台里都很常见。

深色模式怎么处理

最常见的写法还是 class-based。

<html className="dark">
<div className="bg-white text-zinc-900 dark:bg-zinc-950 dark:text-zinc-50" />

如果项目已经在用 next-themes,这一层会更顺。

响应式和容器查询

Tailwind 本来就很适合写响应式。到了 v4 这代,容器查询也值得优先掌握。

<div className="@container">
<div className="grid grid-cols-1 gap-4 @lg:grid-cols-2">
<section className="rounded-2xl border p-4">A</section>
<section className="rounded-2xl border p-4">B</section>
</div>
</div>

如果组件会被放进不同容器宽度里,而不是总跟整页视口走,容器查询会比传统断点更好用。

monorepo 里要注意什么

Tailwind 在 monorepo 里最常见的问题不是“能不能跑”,而是:

  • 业务包和 UI 包的样式扫描边界
  • 多应用共享 token 的位置
  • 是否把 globals.css 和设计 token 独立出来

比较稳的结构通常是:

  • packages/ui:业务组件或通用组件
  • packages/tokens:设计 token / CSS 变量
  • apps/web:真正引入 @import "tailwindcss" 的应用入口

如果共享组件里大量用到了 Tailwind 类名,类名来源所在文件也要纳入样式扫描路径。

常见误区

1. 把 Tailwind 当成“以后都不用写 CSS”

这通常不现实。

更稳的理解是:

  • 高频布局和视觉表达交给 utility classes
  • 设计 token 交给 @theme
  • 少量复杂样式放进 @layer components 或独立 CSS

2. 组件一复杂就把 className 写成一团

Tailwind 的问题不在于类名多,而在于没有及时抽组件、抽 helper、抽 token。

3. 直接照搬旧版 tailwind.config.js 心智模型

v4 之后,很多配置更适合直接进 CSS。继续把一切都塞回 JS 配置文件,反而会把项目写重。

4. 过早引入一堆插件

排版、表单、动画是高频需求;其他扩展最好带着明确场景加,不要一开始就把链路堆满。

一个更实际的采用顺序

  1. 先把安装链路跑通
  2. @theme 收颜色、字体、间距和圆角
  3. 配好 clsx + tailwind-merge
  4. 先做 Button、Card、Input 这批高频组件
  5. 再决定接 HeroUI、Base UI,还是继续手写组件体系

和其他 UI 方案怎么配

  • 想完全掌控视觉:Tailwind + Base UI
  • 想快速拿到成熟组件:Tailwind + HeroUI
  • 想兼顾 React Native:Tailwind + gluestack-ui / NativeWind 体系

所以 Tailwind 很少是“最后的成品”,更常见的是它作为底座,把上层组件体系托起来。

参考来源