第 157 章:Tailwind CSS 样式
学习目标
- 掌握 Tailwind 原子化类名
- 学会响应式与暗色模式
- 理解自定义配置
- 用 cn() 合并类名
一、Tailwind 是什么
Utility-First CSS 框架,直接在 JSX 中写类名,无需写 CSS 文件。
tsx
<button className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
点击
</button>二、安装
Next.js 创建时已选 Tailwind,手动安装:
bash
pnpm add -D tailwindcss postcss autoprefixer
pnpm dlx tailwindcss init -p三、配置
3.1 tailwind.config.ts
ts
import type { Config } from 'tailwindcss';
const config: Config = {
content: [
'./app/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
colors: {
brand: '#42b883',
},
},
},
plugins: [],
};
export default config;3.2 globals.css
css
@tailwind base;
@tailwind components;
@tailwind utilities;四、常用类名
4.1 布局
| 类名 | 作用 |
|---|---|
flex | flex 布局 |
grid | grid 布局 |
block inline-block | 显示 |
hidden | 隐藏 |
mx-auto | 水平居中 |
container | 容器 |
4.2 间距
| 类名 | 含义 |
|---|---|
p-4 | padding 1rem |
px-4 | 左右 padding |
py-2 | 上下 padding |
m-4 | margin 1rem |
mt-2 mb-4 | 单边 |
space-y-2 | 子元素垂直间距 |
4.3 尺寸
| 类名 | 含义 |
|---|---|
w-full | 宽度 100% |
w-1/2 | 50% |
w-32 | 8rem |
h-screen | 视口高度 |
min-h-screen | 最小视口高度 |
max-w-md | 最大宽度 |
4.4 颜色
| 类名 | 含义 |
|---|---|
bg-blue-500 | 背景蓝 |
text-white | 文字白 |
border-gray-200 | 边框灰 |
text-red-500/50 | 红色 50% 透明 |
色阶:50 / 100 / 200 / ... / 900 / 950
4.5 排版
| 类名 | 含义 |
|---|---|
text-sm text-lg text-xl | 字号 |
font-bold font-medium | 字重 |
text-center text-left | 对齐 |
leading-relaxed | 行高 |
truncate | 截断... |
4.6 圆角与阴影
| 类名 | 含义 |
|---|---|
rounded rounded-lg rounded-full | 圆角 |
shadow shadow-md shadow-lg | 阴影 |
五、伪类与状态
tsx
<button className="
bg-blue-500 // 默认
hover:bg-blue-600 // 悬停
active:bg-blue-700 // 按下
focus:outline-none // 聚焦
focus:ring-2 // 聚焦环
disabled:opacity-50 // 禁用
disabled:cursor-not-allowed
">
提交
</button>六、响应式
| 前缀 | 宽度 |
|---|---|
sm: | ≥640px |
md: | ≥768px |
lg: | ≥1024px |
xl: | ≥1280px |
2xl: | ≥1536px |
tsx
<div className="
flex flex-col // 默认:手机端纵向
md:flex-row // 平板及以上:横向
lg:gap-8 // 桌面端间距加大
">
<div>左侧</div>
<div>右侧</div>
</div>七、暗色模式
tsx
<div className="
bg-white
text-black
dark:bg-gray-900
dark:text-white
">
内容
</div>八、cn() 合并类名
用 clsx + tailwind-merge:
bash
pnpm add clsx tailwind-mergetsx
// lib/utils.ts
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}tsx
// 使用
import { cn } from '@/lib/utils';
function Button({ variant, className, ...rest }: ButtonProps) {
return (
<button
className={cn(
'px-4 py-2 rounded',
variant === 'primary' && 'bg-blue-500 text-white',
variant === 'danger' && 'bg-red-500 text-white',
className // 外部传入可覆盖
)}
{...rest}
/>
);
}九、自定义组件
tsx
// components/Card.tsx
import { cn } from '@/lib/utils';
export function Card({ className, children }: { className?: string; children: React.ReactNode }) {
return (
<div className={cn('rounded-lg border p-4 shadow-sm', className)}>
{children}
</div>
);
}十、条件类名
tsx
import { cn } from '@/lib/utils';
function NavLink({ active }: { active: boolean }) {
return (
<a
className={cn(
'px-3 py-2 rounded',
active
? 'bg-blue-500 text-white'
: 'text-gray-700 hover:bg-gray-100'
)}
>
链接
</a>
);
}十一、Tailwind vs CSS-in-JS
| 维度 | Tailwind | CSS-in-JS (styled) |
|---|---|---|
| 学习 | 记类名 | 写 CSS |
| 性能 | 极快(零运行时) | 有运行时开销 |
| 动态 | 需 cn() | 直接 props |
| 维护 | 类名多 | 组件化 |
| 适合 | 通用项目 | 设计系统 |
十二、本章小结
| 概念 | 关键 |
|---|---|
| Utility-First | 直接用类名,无 CSS |
| 响应式 | md: lg: 前缀 |
| 暗色 | dark: 前缀 |
| 状态 | hover: focus: |
| cn() | 合并类名,处理冲突 |
| 自定义 | tailwind.config extend |
动手练习
- 用 Tailwind 实现一个卡片组件
- 实现响应式导航栏(手机端汉堡菜单)
- 实现暗色模式切换
- 用 cn() 封装一个 Button 组件(3 个 variant)
推荐阅读
- 📖 Tailwind 官方文档
- 📖 Tailwind 速查
- 📖 shadcn/ui — 基于 Tailwind 的组件库