官方文档:https://tailwindcss.com/docs
适用版本:Tailwind CSS 4.x(2026-05-07 核实)
相关文档:HTML与CSS入门 Vue3入门
目录
- Tailwind 是什么
- 安装与配置
- 核心工具类速查表
- 自定义配置
- 最佳实践
- 100 个常用工具类速查表
- 综合实战示例
Tailwind 是什么
工具类优先(Utility-First)理念
Tailwind CSS 是一个工具类优先(Utility-First)的 CSS 框架。它不提供预先设计好的组件(如 Bootstrap 的 .btn、.card),而是提供大量低层级的工具类,每个工具类只做一件事。
<!-- 传统 CSS 写法:需要自己起类名,跳转到 CSS 文件写样式 -->
<div class="card">...</div>
<!-- Bootstrap 写法:使用预设组件,样式固定,难以定制 -->
<div class="card p-3 shadow-sm">...</div>
<!-- Tailwind 写法:工具类直接描述样式,不需要额外 CSS 文件 -->
<div class="flex flex-col p-6 bg-white rounded-xl shadow-md">...</div>
与传统 CSS / Bootstrap 的对比
| 维度 |
传统 CSS |
Bootstrap |
Tailwind CSS |
| 写法 |
自定义类名 + CSS 文件 |
预设组件类名 |
工具类直接写在 HTML |
| 定制性 |
完全自由 |
需覆盖变量或重写 |
通过配置文件扩展 |
| 包体积 |
手写多少就多少 |
引入全部组件样式 |
PurgeCSS 按需打包 |
| 学习曲线 |
需要懂 CSS |
记住组件名 |
需要记住工具类名 |
| 一致性 |
依赖开发者自律 |
组件一致 |
设计系统约束一致 |
| 响应式 |
手写 media query |
内置断点类 |
前缀断点类(sm/md/lg) |
| 暗色模式 |
手写 prefers-color-scheme |
需额外插件 |
dark: 变体内置支持 |
核心优势:
- 不需要在 HTML 和 CSS 文件之间来回切换
- 不需要绞尽脑汁起类名
- CSS 文件大小不随项目增长而无限膨胀(工具类是复用的)
- 设计约束来自配置文件,全局一致
安装与配置
方式一:CDN(仅用于开发调试)
<!-- 在 <head> 中引入,无需安装,无法自定义配置 -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Tailwind CSS v4 CDN(最新版本) -->
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<h1 class="text-3xl font-bold text-blue-600">Hello Tailwind</h1>
</body>
</html>
注意:CDN 方式会加载完整的 Tailwind CSS(约 4MB),不适合生产环境。
方式二:Vite 项目集成(推荐)
# 创建 Vite 项目(以 Vue3 为例)
npm create vite@latest my-project -- --template vue
cd my-project
# 安装 Tailwind CSS 及依赖
npm install -D tailwindcss postcss autoprefixer
# 生成配置文件
npx tailwindcss init -p
生成的 postcss.config.js:
// postcss.config.js
export default {
plugins: {
tailwindcss: {}, // 处理 Tailwind 指令
autoprefixer: {}, // 自动添加浏览器前缀
},
}
在 tailwind.config.js 中配置内容扫描路径:
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
// content:告诉 Tailwind 扫描哪些文件,提取使用到的工具类
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
在主 CSS 文件中引入 Tailwind 指令:
/* src/style.css 或 src/assets/main.css */
/* 引入 Tailwind 的基础样式(reset + 基础排版) */
@tailwind base;
/* 引入所有工具类 */
@tailwind components;
/* 引入所有工具类 */
@tailwind utilities;
在 main.js 中引入 CSS:
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import './style.css' // 引入包含 Tailwind 指令的 CSS 文件
createApp(App).mount('#app')
方式三:PostCSS 配置(非 Vite 项目)
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init
在构建工具(Webpack/Rollup 等)的 PostCSS 配置中添加 Tailwind 插件,与方式二类似。
tailwind.config.js 完整结构说明
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
// content:文件扫描路径,Tailwind 会从这些文件中提取用到的类名
// 生产构建时会删除未使用的工具类
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx,html}",
],
// darkMode:暗色模式策略
// 'media' → 跟随系统 prefers-color-scheme(默认)
// 'class' → 手动在 <html> 上添加 .dark 类来切换
darkMode: 'class',
theme: {
// theme 直接赋值会覆盖默认主题(谨慎使用)
// fontFamily: { sans: ['Arial', 'sans-serif'] } // 完全替换字体
// extend 在默认主题基础上追加,推荐使用
extend: {
colors: {
brand: {
50: '#eff6ff',
500: '#3b82f6',
900: '#1e3a8a',
},
},
fontFamily: {
custom: ['"Microsoft YaHei"', 'sans-serif'],
},
spacing: {
'18': '4.5rem', // 追加 m-18, p-18 等工具类
'72': '18rem',
},
borderRadius: {
'4xl': '2rem',
},
screens: {
'xs': '475px', // 追加 xs: 断点
},
},
},
// plugins:引入第三方插件
plugins: [
// require('@tailwindcss/forms'), // 表单样式重置
// require('@tailwindcss/typography'), // 文章排版(prose 类)
// require('@tailwindcss/aspect-ratio'),
],
}
核心工具类速查表
布局
Container
| 类名 |
说明 |
container |
设置 max-width 并自动响应断点,需配合 mx-auto 居中 |
<div class="container mx-auto px-4">
<!-- 内容区域,居中且两侧有内边距 -->
</div>
Box Sizing
| 类名 |
CSS 等价 |
box-border |
box-sizing: border-box |
box-content |
box-sizing: content-box |
Display
| 类名 |
CSS 等价 |
block |
display: block |
inline |
display: inline |
inline-block |
display: inline-block |
flex |
display: flex |
inline-flex |
display: inline-flex |
grid |
display: grid |
inline-grid |
display: inline-grid |
hidden |
display: none |
table |
display: table |
table-cell |
display: table-cell |
contents |
display: contents |
Overflow
| 类名 |
CSS 等价 |
overflow-auto |
overflow: auto |
overflow-hidden |
overflow: hidden |
overflow-visible |
overflow: visible |
overflow-scroll |
overflow: scroll |
overflow-x-auto |
overflow-x: auto |
overflow-y-auto |
overflow-y: auto |
overflow-x-hidden |
overflow-x: hidden |
truncate |
单行文字截断(overflow-hidden + text-ellipsis + whitespace-nowrap) |
Position
| 类名 |
CSS 等价 |
static |
position: static |
relative |
position: relative |
absolute |
position: absolute |
fixed |
position: fixed |
sticky |
position: sticky |
定位偏移(与 position 配合使用):
| 类名 |
CSS 等价 |
top-0 |
top: 0 |
top-4 |
top: 1rem |
right-0 |
right: 0 |
bottom-0 |
bottom: 0 |
left-0 |
left: 0 |
inset-0 |
top/right/bottom/left: 0(常用于覆盖层) |
inset-x-0 |
left: 0; right: 0 |
inset-y-0 |
top: 0; bottom: 0 |
top-1/2 |
top: 50%(配合 -translate-y-1/2 垂直居中) |
Z-Index
| 类名 |
CSS 等价 |
z-0 |
z-index: 0 |
z-10 |
z-index: 10 |
z-20 |
z-index: 20 |
z-30 |
z-index: 30 |
z-40 |
z-index: 40 |
z-50 |
z-index: 50 |
z-auto |
z-index: auto |
-z-10 |
z-index: -10 |
Flexbox
<!-- 基础 Flex 容器 -->
<div class="flex items-center justify-between gap-4">
<div>左侧内容</div>
<div>右侧内容</div>
</div>
flex-direction(主轴方向)
| 类名 |
CSS 等价 |
flex-row |
flex-direction: row(默认,水平从左到右) |
flex-row-reverse |
flex-direction: row-reverse |
flex-col |
flex-direction: column(垂直从上到下) |
flex-col-reverse |
flex-direction: column-reverse |
justify-content(主轴对齐)
| 类名 |
CSS 等价 |
justify-start |
justify-content: flex-start |
justify-end |
justify-content: flex-end |
justify-center |
justify-content: center |
justify-between |
justify-content: space-between |
justify-around |
justify-content: space-around |
justify-evenly |
justify-content: space-evenly |
align-items(交叉轴对齐)
| 类名 |
CSS 等价 |
items-start |
align-items: flex-start |
items-end |
align-items: flex-end |
items-center |
align-items: center |
items-baseline |
align-items: baseline |
items-stretch |
align-items: stretch |
align-self(单个子项交叉轴对齐)
| 类名 |
CSS 等价 |
self-auto |
align-self: auto |
self-start |
align-self: flex-start |
self-end |
align-self: flex-end |
self-center |
align-self: center |
self-stretch |
align-self: stretch |
flex-wrap
| 类名 |
CSS 等价 |
flex-nowrap |
flex-wrap: nowrap(默认) |
flex-wrap |
flex-wrap: wrap |
flex-wrap-reverse |
flex-wrap: wrap-reverse |
gap(间距)
| 类名 |
CSS 等价 |
gap-0 |
gap: 0 |
gap-1 |
gap: 0.25rem |
gap-2 |
gap: 0.5rem |
gap-4 |
gap: 1rem |
gap-6 |
gap: 1.5rem |
gap-8 |
gap: 2rem |
gap-x-4 |
column-gap: 1rem |
gap-y-4 |
row-gap: 1rem |
flex-grow / flex-shrink
| 类名 |
CSS 等价 |
flex-1 |
flex: 1 1 0%(等比分配剩余空间) |
flex-auto |
flex: 1 1 auto |
flex-none |
flex: none(不参与弹性计算) |
grow |
flex-grow: 1 |
grow-0 |
flex-grow: 0 |
shrink |
flex-shrink: 1 |
shrink-0 |
flex-shrink: 0(不压缩,常用于固定宽度元素) |
order(排列顺序)
| 类名 |
CSS 等价 |
order-first |
order: -9999 |
order-last |
order: 9999 |
order-none |
order: 0 |
order-1 ~ order-12 |
order: 1 ~ order: 12 |
Grid
<!-- 三列等宽网格,间距 1rem -->
<div class="grid grid-cols-3 gap-4">
<div>列1</div>
<div>列2</div>
<div>列3</div>
</div>
grid-cols(列数)
| 类名 |
CSS 等价 |
grid-cols-1 |
grid-template-columns: repeat(1, minmax(0, 1fr)) |
grid-cols-2 |
grid-template-columns: repeat(2, minmax(0, 1fr)) |
grid-cols-3 |
grid-template-columns: repeat(3, minmax(0, 1fr)) |
grid-cols-4 |
grid-template-columns: repeat(4, minmax(0, 1fr)) |
grid-cols-6 |
grid-template-columns: repeat(6, minmax(0, 1fr)) |
grid-cols-12 |
grid-template-columns: repeat(12, minmax(0, 1fr)) |
grid-cols-none |
grid-template-columns: none |
col-span(列跨越)
| 类名 |
CSS 等价 |
col-span-1 |
grid-column: span 1 / span 1 |
col-span-2 |
grid-column: span 2 / span 2 |
col-span-3 |
grid-column: span 3 / span 3 |
col-span-full |
grid-column: 1 / -1(跨越所有列) |
col-start-1 |
grid-column-start: 1 |
col-end-4 |
grid-column-end: 4 |
grid-rows(行数)
| 类名 |
CSS 等价 |
grid-rows-1 |
grid-template-rows: repeat(1, minmax(0, 1fr)) |
grid-rows-3 |
grid-template-rows: repeat(3, minmax(0, 1fr)) |
grid-rows-6 |
grid-template-rows: repeat(6, minmax(0, 1fr)) |
row-span(行跨越)
| 类名 |
CSS 等价 |
row-span-1 |
grid-row: span 1 / span 1 |
row-span-2 |
grid-row: span 2 / span 2 |
row-span-full |
grid-row: 1 / -1 |
Spacing(间距)
间距尺寸表
| 比例值 N |
rem 值 |
px 值(16px 基准) |
| 0 |
0 |
0 |
| 0.5 |
0.125rem |
2px |
| 1 |
0.25rem |
4px |
| 1.5 |
0.375rem |
6px |
| 2 |
0.5rem |
8px |
| 2.5 |
0.625rem |
10px |
| 3 |
0.75rem |
12px |
| 3.5 |
0.875rem |
14px |
| 4 |
1rem |
16px |
| 5 |
1.25rem |
20px |
| 6 |
1.5rem |
24px |
| 7 |
1.75rem |
28px |
| 8 |
2rem |
32px |
| 9 |
2.25rem |
36px |
| 10 |
2.5rem |
40px |
| 11 |
2.75rem |
44px |
| 12 |
3rem |
48px |
| 14 |
3.5rem |
56px |
| 16 |
4rem |
64px |
| 20 |
5rem |
80px |
| 24 |
6rem |
96px |
| 28 |
7rem |
112px |
| 32 |
8rem |
128px |
| 36 |
9rem |
144px |
| 40 |
10rem |
160px |
| 44 |
11rem |
176px |
| 48 |
12rem |
192px |
| 52 |
13rem |
208px |
| 56 |
14rem |
224px |
| 60 |
15rem |
240px |
| 64 |
16rem |
256px |
| 72 |
18rem |
288px |
| 80 |
20rem |
320px |
| 96 |
24rem |
384px |
Margin 工具类
| 类名 |
说明 |
m-{N} |
四边 margin |
mx-{N} |
水平方向 margin(left + right) |
my-{N} |
垂直方向 margin(top + bottom) |
mt-{N} |
margin-top |
mb-{N} |
margin-bottom |
ml-{N} |
margin-left |
mr-{N} |
margin-right |
ms-{N} |
margin-inline-start(支持 RTL) |
me-{N} |
margin-inline-end(支持 RTL) |
mx-auto |
水平居中(margin-left/right: auto) |
-m-{N} |
负 margin(如 -m-4) |
Padding 工具类
| 类名 |
说明 |
p-{N} |
四边 padding |
px-{N} |
水平方向 padding(left + right) |
py-{N} |
垂直方向 padding(top + bottom) |
pt-{N} |
padding-top |
pb-{N} |
padding-bottom |
pl-{N} |
padding-left |
pr-{N} |
padding-right |
Sizing(尺寸)
Width
| 类名 |
CSS 等价 |
w-0 |
width: 0 |
w-{N} |
width: {N 对应的 rem 值} |
w-auto |
width: auto |
w-full |
width: 100% |
w-screen |
width: 100vw |
w-svw |
width: 100svw(small viewport) |
w-fit |
width: fit-content |
w-min |
width: min-content |
w-max |
width: max-content |
w-1/2 |
width: 50% |
w-1/3 |
width: 33.333% |
w-2/3 |
width: 66.667% |
w-1/4 |
width: 25% |
w-3/4 |
width: 75% |
Max-Width / Min-Width
| 类名 |
CSS 等价 |
max-w-xs |
max-width: 20rem(320px) |
max-w-sm |
max-width: 24rem(384px) |
max-w-md |
max-width: 28rem(448px) |
max-w-lg |
max-width: 32rem(512px) |
max-w-xl |
max-width: 36rem(576px) |
max-w-2xl |
max-width: 42rem(672px) |
max-w-3xl |
max-width: 48rem(768px) |
max-w-4xl |
max-width: 56rem(896px) |
max-w-5xl |
max-width: 64rem(1024px) |
max-w-6xl |
max-width: 72rem(1152px) |
max-w-7xl |
max-width: 80rem(1280px) |
max-w-full |
max-width: 100% |
max-w-screen-sm |
max-width: 640px |
max-w-screen-lg |
max-width: 1024px |
min-w-0 |
min-width: 0 |
min-w-full |
min-width: 100% |
Height
| 类名 |
CSS 等价 |
h-{N} |
height: {N 对应的 rem 值} |
h-auto |
height: auto |
h-full |
height: 100% |
h-screen |
height: 100vh |
h-svh |
height: 100svh |
h-dvh |
height: 100dvh(动态视口,推荐移动端) |
h-fit |
height: fit-content |
h-min |
height: min-content |
h-max |
height: max-content |
min-h-0 |
min-height: 0 |
min-h-full |
min-height: 100% |
min-h-screen |
min-height: 100vh |
max-h-full |
max-height: 100% |
max-h-screen |
max-height: 100vh |
Typography(排版)
Font Size
| 类名 |
font-size |
line-height |
text-xs |
0.75rem (12px) |
1rem |
text-sm |
0.875rem (14px) |
1.25rem |
text-base |
1rem (16px) |
1.5rem |
text-lg |
1.125rem (18px) |
1.75rem |
text-xl |
1.25rem (20px) |
1.75rem |
text-2xl |
1.5rem (24px) |
2rem |
text-3xl |
1.875rem (30px) |
2.25rem |
text-4xl |
2.25rem (36px) |
2.5rem |
text-5xl |
3rem (48px) |
1 |
text-6xl |
3.75rem (60px) |
1 |
text-7xl |
4.5rem (72px) |
1 |
text-8xl |
6rem (96px) |
1 |
text-9xl |
8rem (128px) |
1 |
Font Weight
| 类名 |
font-weight |
font-thin |
100 |
font-extralight |
200 |
font-light |
300 |
font-normal |
400 |
font-medium |
500 |
font-semibold |
600 |
font-bold |
700 |
font-extrabold |
800 |
font-black |
900 |
Font Family
| 类名 |
CSS 等价 |
font-sans |
系统 sans-serif 字体栈 |
font-serif |
系统 serif 字体栈 |
font-mono |
系统等宽字体栈 |
Text Align
| 类名 |
CSS 等价 |
text-left |
text-align: left |
text-center |
text-align: center |
text-right |
text-align: right |
text-justify |
text-align: justify |
Text Color
颜色格式:text-{color}-{shade},shade 范围 50/100/200/300/400/500/600/700/800/900/950
<!-- 常用颜色示例 -->
<p class="text-gray-500">灰色文字</p>
<p class="text-blue-600">蓝色文字</p>
<p class="text-red-500">红色文字</p>
<p class="text-green-700">深绿文字</p>
<p class="text-white">白色文字</p>
<p class="text-black">黑色文字</p>
<p class="text-transparent">透明文字</p>
内置颜色:slate / gray / zinc / neutral / stone / red / orange / amber / yellow / lime / green / emerald / teal / cyan / sky / blue / indigo / violet / purple / fuchsia / pink / rose
Line Height(leading)
| 类名 |
line-height |
leading-none |
1 |
leading-tight |
1.25 |
leading-snug |
1.375 |
leading-normal |
1.5 |
leading-relaxed |
1.625 |
leading-loose |
2 |
leading-3 |
0.75rem |
leading-4 |
1rem |
leading-5 |
1.25rem |
Letter Spacing(tracking)
| 类名 |
letter-spacing |
tracking-tighter |
-0.05em |
tracking-tight |
-0.025em |
tracking-normal |
0em |
tracking-wide |
0.025em |
tracking-wider |
0.05em |
tracking-widest |
0.1em |
Text Decoration
| 类名 |
CSS 等价 |
underline |
text-decoration: underline |
overline |
text-decoration: overline |
line-through |
text-decoration: line-through |
no-underline |
text-decoration: none |
Text Transform
| 类名 |
CSS 等价 |
uppercase |
text-transform: uppercase |
lowercase |
text-transform: lowercase |
capitalize |
text-transform: capitalize |
normal-case |
text-transform: none |
Whitespace & Word Break
| 类名 |
CSS 等价 |
whitespace-normal |
white-space: normal |
whitespace-nowrap |
white-space: nowrap |
whitespace-pre |
white-space: pre |
whitespace-pre-wrap |
white-space: pre-wrap |
break-normal |
overflow-wrap: normal |
break-words |
overflow-wrap: break-word |
break-all |
word-break: break-all |
Background(背景)
Background Color
<div class="bg-blue-500">蓝色背景</div>
<div class="bg-gray-100">浅灰背景</div>
<div class="bg-white">白色背景</div>
<div class="bg-transparent">透明背景</div>
格式:bg-{color}-{shade},与文字颜色体系一致。
Background Opacity(Tailwind v3 用法)
<!-- v3 中通过 / 语法设置透明度 -->
<div class="bg-blue-500/50">50% 透明度的蓝色背景</div>
<div class="bg-black/30">30% 透明度的黑色(常用于遮罩层)</div>
Background Gradient
<!-- 水平渐变:左到右 -->
<div class="bg-gradient-to-r from-blue-500 to-purple-600">渐变背景</div>
<!-- 三色渐变:含中间色 -->
<div class="bg-gradient-to-r from-pink-500 via-red-500 to-yellow-500">三色渐变</div>
<!-- 对角线渐变 -->
<div class="bg-gradient-to-br from-green-400 to-blue-500">对角渐变</div>
渐变方向:to-t(上)/ to-tr(右上)/ to-r(右)/ to-br(右下)/ to-b(下)/ to-bl(左下)/ to-l(左)/ to-tl(左上)
Background Size / Position
| 类名 |
CSS 等价 |
bg-auto |
background-size: auto |
bg-cover |
background-size: cover |
bg-contain |
background-size: contain |
bg-center |
background-position: center |
bg-top |
background-position: top |
bg-bottom |
background-position: bottom |
bg-left |
background-position: left |
bg-right |
background-position: right |
bg-no-repeat |
background-repeat: no-repeat |
Border(边框)
Border Width
| 类名 |
CSS 等价 |
border |
border-width: 1px |
border-0 |
border-width: 0 |
border-2 |
border-width: 2px |
border-4 |
border-width: 4px |
border-8 |
border-width: 8px |
border-t |
border-top-width: 1px |
border-b |
border-bottom-width: 1px |
border-l |
border-left-width: 1px |
border-r |
border-right-width: 1px |
border-x |
左右各 1px |
border-y |
上下各 1px |
Border Color
<div class="border border-gray-300">灰色边框</div>
<div class="border-2 border-blue-500">蓝色边框</div>
格式:border-{color}-{shade}
Border Style
| 类名 |
CSS 等价 |
border-solid |
border-style: solid |
border-dashed |
border-style: dashed |
border-dotted |
border-style: dotted |
border-double |
border-style: double |
border-none |
border-style: none |
Border Radius(圆角)
| 类名 |
CSS 等价 |
rounded-none |
border-radius: 0 |
rounded-sm |
border-radius: 0.125rem |
rounded |
border-radius: 0.25rem |
rounded-md |
border-radius: 0.375rem |
rounded-lg |
border-radius: 0.5rem |
rounded-xl |
border-radius: 0.75rem |
rounded-2xl |
border-radius: 1rem |
rounded-3xl |
border-radius: 1.5rem |
rounded-full |
border-radius: 9999px(圆形/胶囊) |
rounded-t-lg |
上方两角 |
rounded-b-lg |
下方两角 |
rounded-l-lg |
左侧两角 |
rounded-r-lg |
右侧两角 |
rounded-tl-lg |
左上角 |
rounded-tr-lg |
右上角 |
Outline
| 类名 |
CSS 等价 |
outline |
outline-style: solid |
outline-none |
outline: 2px solid transparent(消除默认轮廓) |
outline-dashed |
outline-style: dashed |
outline-2 |
outline-width: 2px |
outline-offset-2 |
outline-offset: 2px |
Effects(效果)
Box Shadow
| 类名 |
说明 |
shadow-sm |
细微阴影 |
shadow |
基础阴影 |
shadow-md |
中等阴影 |
shadow-lg |
较大阴影 |
shadow-xl |
大阴影 |
shadow-2xl |
超大阴影 |
shadow-inner |
内阴影 |
shadow-none |
无阴影 |
Opacity
| 类名 |
CSS 等价 |
opacity-0 |
opacity: 0 |
opacity-5 |
opacity: 0.05 |
opacity-10 |
opacity: 0.1 |
opacity-25 |
opacity: 0.25 |
opacity-50 |
opacity: 0.5 |
opacity-75 |
opacity: 0.75 |
opacity-90 |
opacity: 0.9 |
opacity-100 |
opacity: 1 |
Ring(焦点轮廓,常用于交互反馈)
| 类名 |
说明 |
ring |
3px 蓝色轮廓(box-shadow 实现) |
ring-0 |
无轮廓 |
ring-1 |
1px 轮廓 |
ring-2 |
2px 轮廓 |
ring-4 |
4px 轮廓 |
ring-blue-500 |
蓝色轮廓 |
ring-offset-2 |
轮廓与元素间距 2px |
Transitions & Animation(过渡与动画)
Transition
| 类名 |
说明 |
transition |
过渡 color/background/border/opacity/box-shadow/transform |
transition-all |
过渡所有可动画属性 |
transition-colors |
仅过渡颜色相关属性 |
transition-opacity |
仅过渡透明度 |
transition-transform |
仅过渡变换 |
transition-none |
禁用过渡 |
Duration(过渡时长)
| 类名 |
CSS 等价 |
duration-75 |
transition-duration: 75ms |
duration-100 |
transition-duration: 100ms |
duration-150 |
transition-duration: 150ms |
duration-200 |
transition-duration: 200ms |
duration-300 |
transition-duration: 300ms |
duration-500 |
transition-duration: 500ms |
duration-700 |
transition-duration: 700ms |
duration-1000 |
transition-duration: 1000ms |
Easing(缓动函数)
| 类名 |
CSS 等价 |
ease-linear |
transition-timing-function: linear |
ease-in |
transition-timing-function: cubic-bezier(0.4, 0, 1, 1) |
ease-out |
transition-timing-function: cubic-bezier(0, 0, 0.2, 1) |
ease-in-out |
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1) |
| 类名 |
CSS 等价 |
scale-0 |
transform: scale(0) |
scale-50 |
transform: scale(0.5) |
scale-100 |
transform: scale(1) |
scale-105 |
transform: scale(1.05) |
scale-110 |
transform: scale(1.1) |
rotate-0 |
transform: rotate(0deg) |
rotate-45 |
transform: rotate(45deg) |
rotate-90 |
transform: rotate(90deg) |
rotate-180 |
transform: rotate(180deg) |
translate-x-4 |
transform: translateX(1rem) |
translate-y-4 |
transform: translateY(1rem) |
-translate-y-1/2 |
transform: translateY(-50%)(垂直居中常用) |
skew-x-6 |
transform: skewX(6deg) |
Animation(预设动画)
| 类名 |
效果 |
animate-spin |
持续旋转(常用于加载图标) |
animate-ping |
扩散消失(常用于通知红点) |
animate-pulse |
呼吸明暗(常用于骨架屏) |
animate-bounce |
上下弹跳 |
animate-none |
禁用动画 |
<!-- 加载中旋转图标 -->
<svg class="animate-spin h-5 w-5 text-blue-500" viewBox="0 0 24 24">...</svg>
<!-- 骨架屏占位 -->
<div class="animate-pulse bg-gray-200 h-4 w-full rounded"></div>
Interactivity(交互)
Cursor
| 类名 |
CSS 等价 |
cursor-auto |
cursor: auto |
cursor-default |
cursor: default |
cursor-pointer |
cursor: pointer(手型) |
cursor-wait |
cursor: wait |
cursor-text |
cursor: text |
cursor-move |
cursor: move |
cursor-not-allowed |
cursor: not-allowed |
cursor-grab |
cursor: grab |
cursor-grabbing |
cursor: grabbing |
User Select
| 类名 |
CSS 等价 |
select-none |
user-select: none |
select-text |
user-select: text |
select-all |
user-select: all |
select-auto |
user-select: auto |
Pointer Events
| 类名 |
CSS 等价 |
pointer-events-none |
pointer-events: none(穿透点击) |
pointer-events-auto |
pointer-events: auto |
响应式前缀(断点)
Tailwind 采用移动端优先策略:不加前缀 = 所有屏幕,加前缀 = 该断点及以上。
断点表格
| 前缀 |
最小宽度 |
对应设备参考 |
| 无前缀 |
0px |
手机(移动端优先基础样式) |
sm: |
640px |
大屏手机 / 小平板 |
md: |
768px |
平板 |
lg: |
1024px |
笔记本 |
xl: |
1280px |
桌面显示器 |
2xl: |
1536px |
大型显示器 |
<!-- 移动端单列,平板两列,桌面四列 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
...
</div>
<!-- 移动端隐藏,桌面显示 -->
<div class="hidden lg:block">仅桌面可见</div>
<!-- 移动端显示,桌面隐藏 -->
<div class="block lg:hidden">仅移动端可见</div>
<!-- 响应式文字大小 -->
<h1 class="text-2xl md:text-4xl lg:text-6xl">标题</h1>
状态变体(State Variants)
格式:{变体}:{工具类},可叠加多个变体。
常用状态变体
| 变体 |
触发条件 |
hover: |
鼠标悬停 |
focus: |
元素获得焦点 |
focus-within: |
子元素获得焦点 |
focus-visible: |
键盘焦点(非鼠标) |
active: |
鼠标按下 |
visited: |
链接已访问 |
disabled: |
元素被禁用 |
checked: |
checkbox/radio 被选中 |
placeholder: |
input 的占位符 |
required: |
必填表单元素 |
invalid: |
验证失败的表单元素 |
valid: |
验证通过的表单元素 |
dark: |
暗色模式 |
print: |
打印时 |
<!-- hover 改变背景色,focus 显示轮廓 -->
<button class="bg-blue-500 hover:bg-blue-700 focus:ring-2 focus:ring-blue-300 transition-colors">
按钮
</button>
<!-- disabled 状态样式 -->
<button class="bg-blue-500 disabled:bg-gray-300 disabled:cursor-not-allowed" disabled>
禁用按钮
</button>
<!-- 暗色模式 -->
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
自适应明暗模式
</div>
Group 变体(父级悬停影响子级)
<!-- group-hover:父元素 hover 时,子元素响应变化 -->
<div class="group flex items-center gap-3 p-4 hover:bg-blue-50 rounded-lg cursor-pointer">
<div class="text-gray-600 group-hover:text-blue-600">
图标区域
</div>
<div class="text-sm text-gray-500 group-hover:text-blue-800">
文字描述
</div>
</div>
Peer 变体(兄弟元素状态影响当前元素)
<!-- peer-focus:前一个兄弟元素获得焦点时,当前元素响应 -->
<input class="peer border rounded px-3 py-2" type="email" placeholder="输入邮箱">
<p class="hidden peer-invalid:block text-red-500 text-sm">邮箱格式不正确</p>
伪元素变体
| 变体 |
说明 |
before: |
::before 伪元素 |
after: |
::after 伪元素 |
first: |
第一个子元素 |
last: |
最后一个子元素 |
odd: |
奇数子元素 |
even: |
偶数子元素 |
first-of-type: |
同类型第一个 |
last-of-type: |
同类型最后一个 |
only-child: |
唯一子元素 |
placeholder: |
input 占位符样式 |
selection: |
用户选中的文字 |
<!-- before/after 伪元素需要加 content-[''] -->
<div class="before:content-[''] before:block before:w-4 before:h-4 before:bg-blue-500">
带前置装饰的元素
</div>
<!-- 列表斑马纹 -->
<ul>
<li class="odd:bg-gray-50 even:bg-white px-4 py-2">奇数行</li>
<li class="odd:bg-gray-50 even:bg-white px-4 py-2">偶数行</li>
</ul>
<!-- 去除最后一个子元素的边框 -->
<div class="last:border-b-0 border-b border-gray-200 py-3">列表项</div>
自定义配置
扩展主题(extend)
// tailwind.config.js
export default {
theme: {
extend: {
// 扩展颜色(追加,不替换默认颜色)
colors: {
brand: {
light: '#60a5fa',
DEFAULT: '#3b82f6', // 使用时写 text-brand(无需加 -DEFAULT)
dark: '#1d4ed8',
},
'custom-gray': '#6b7280',
},
// 扩展字体
fontFamily: {
heading: ['"Noto Serif SC"', 'serif'],
body: ['"Noto Sans SC"', 'sans-serif'],
},
// 扩展间距(追加到默认间距尺寸中)
spacing: {
'13': '3.25rem',
'15': '3.75rem',
'128': '32rem',
'144': '36rem',
},
// 扩展断点
screens: {
'xs': '475px',
'3xl': '1920px',
},
// 扩展圆角
borderRadius: {
'4xl': '2rem',
'5xl': '2.5rem',
},
// 扩展阴影
boxShadow: {
'card': '0 2px 8px rgba(0, 0, 0, 0.08)',
'popup': '0 10px 40px rgba(0, 0, 0, 0.16)',
},
// 扩展 z-index
zIndex: {
'60': '60',
'70': '70',
'80': '80',
'90': '90',
'100': '100',
},
// 扩展动画
animation: {
'fade-in': 'fadeIn 0.3s ease-in-out',
'slide-up': 'slideUp 0.3s ease-out',
},
// 扩展 keyframes(配合 animation 使用)
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
},
},
},
}
@layer 指令
@layer 将自定义样式注入到 Tailwind 的层级中,确保正确的优先级顺序。
/* src/style.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* @layer base:基础重置样式,优先级最低 */
@layer base {
/* 全局字体设置 */
html {
font-family: "Noto Sans SC", sans-serif;
}
/* 重置标题样式 */
h1, h2, h3, h4, h5, h6 {
@apply font-bold leading-tight;
}
/* 重置链接样式 */
a {
@apply text-blue-600 hover:text-blue-800 transition-colors;
}
}
/* @layer components:组件级样式,可被工具类覆盖 */
@layer components {
/* 使用 @apply 抽取重复的工具类组合 */
.btn {
@apply inline-flex items-center justify-center px-4 py-2 rounded-lg font-medium transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2;
}
.btn-primary {
@apply btn bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500;
}
.btn-secondary {
@apply btn bg-gray-100 text-gray-700 hover:bg-gray-200 focus:ring-gray-400;
}
.card {
@apply bg-white rounded-xl shadow-md overflow-hidden;
}
.input {
@apply w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition;
}
}
/* @layer utilities:自定义工具类,优先级最高 */
@layer utilities {
/* 自定义工具类,可被响应式/状态变体修饰 */
.text-balance {
text-wrap: balance;
}
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
}
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
}
@apply 抽取可复用样式
/* 将常用的工具类组合提取为语义化类名 */
@layer components {
/* 导航栏链接 */
.nav-link {
@apply text-gray-600 hover:text-blue-600 font-medium px-3 py-2 rounded-md transition-colors duration-150;
}
/* 标签/徽章 */
.badge {
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
}
.badge-blue {
@apply badge bg-blue-100 text-blue-800;
}
.badge-green {
@apply badge bg-green-100 text-green-800;
}
}
注意事项:
@apply 会在构建时内联展开,不会生成额外的 CSS 类引用
- 避免用
@apply 复制大量工具类,应优先通过组件化解决复用问题
@apply 只能在 CSS 文件中使用,不能在 style 属性中使用
Arbitrary Values(任意值)
当预设值不满足需求时,使用方括号语法输入任意值:
<!-- 任意宽度 -->
<div class="w-[200px]">固定 200px 宽度</div>
<div class="w-[calc(100%-2rem)]">计算宽度</div>
<!-- 任意颜色 -->
<p class="text-[#ff6b6b]">自定义红色文字</p>
<div class="bg-[rgb(59,130,246)]">自定义背景色</div>
<!-- 任意字体大小 -->
<p class="text-[13px]">13px 字体</p>
<!-- 任意 z-index -->
<div class="z-[999]">高层级定位</div>
<!-- 任意网格列定义 -->
<div class="grid grid-cols-[200px_1fr_auto]">不等宽三列</div>
<!-- 任意行高 -->
<p class="leading-[1.8]">1.8 倍行高</p>
<!-- CSS 变量 -->
<div class="bg-[var(--primary-color)]">使用 CSS 变量</div>
最佳实践
组件化:避免重复的工具类堆叠
<!-- 不推荐:同样的按钮样式散落在各处 -->
<template>
<button class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">提交</button>
<button class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">确认</button>
</template>
<!-- 推荐方案一:封装为 Vue 组件 -->
<!-- components/BaseButton.vue -->
<template>
<button
:class="[
'inline-flex items-center justify-center px-4 py-2 rounded-lg font-medium transition-colors',
variants[variant],
sizes[size],
{ 'opacity-50 cursor-not-allowed': disabled }
]"
:disabled="disabled"
>
<slot />
</button>
</template>
<script setup>
defineProps({
variant: { type: String, default: 'primary' },
size: { type: String, default: 'md' },
disabled: { type: Boolean, default: false },
})
const variants = {
primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-2 focus:ring-blue-500',
secondary: 'bg-gray-100 text-gray-700 hover:bg-gray-200',
danger: 'bg-red-600 text-white hover:bg-red-700',
ghost: 'text-gray-700 hover:bg-gray-100',
}
const sizes = {
sm: 'text-sm px-3 py-1.5',
md: 'text-base px-4 py-2',
lg: 'text-lg px-6 py-3',
}
</script>
<!-- 推荐方案二:在 CSS 中用 @apply 提取 -->
/* @layer components { .btn-primary { @apply ... } } */
暗色模式
class 策略(推荐,可手动切换):
// tailwind.config.js
export default {
darkMode: 'class', // 通过 .dark 类控制
// ...
}
// 切换暗色模式
function toggleDarkMode() {
document.documentElement.classList.toggle('dark')
// 持久化到 localStorage
localStorage.setItem('theme',
document.documentElement.classList.contains('dark') ? 'dark' : 'light'
)
}
// 初始化时读取用户偏好
const savedTheme = localStorage.getItem('theme')
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
if (savedTheme === 'dark' || (!savedTheme && prefersDark)) {
document.documentElement.classList.add('dark')
}
<!-- 同时支持明暗模式的组件 -->
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 transition-colors duration-300">
<nav class="border-b border-gray-200 dark:border-gray-700">
<a class="text-gray-600 dark:text-gray-300 hover:text-blue-600 dark:hover:text-blue-400">链接</a>
</nav>
</div>
media 策略(跟随系统,无法手动切换):
// tailwind.config.js
export default {
darkMode: 'media', // 跟随系统 prefers-color-scheme
}
响应式设计:移动端优先
<!-- 正确:从最小屏开始,逐步增强 -->
<div class="
flex flex-col gap-4
md:flex-row md:gap-6
lg:gap-8
">
<aside class="w-full md:w-64 lg:w-80">侧边栏</aside>
<main class="flex-1">主内容</main>
</div>
<!-- 正确:图片在移动端全宽,桌面端固定宽度 -->
<img class="w-full lg:w-1/2">
<!-- 错误:先写桌面样式再覆盖移动端(反向覆盖难维护)-->
<!-- <div class="flex-row max-sm:flex-col"> 不推荐这种反向覆盖 -->
与 Vue3 结合的动态 class 绑定
<template>
<!-- 方式一:三元表达式 -->
<div :class="isActive ? 'bg-blue-500 text-white' : 'bg-gray-100 text-gray-700'">
状态切换
</div>
<!-- 方式二:对象语法 -->
<div :class="{
'bg-blue-500': isActive,
'text-white': isActive,
'opacity-50': isLoading,
'cursor-not-allowed': disabled,
}">
复杂状态
</div>
<!-- 方式三:数组语法(推荐,清晰) -->
<div :class="[
'base-class px-4 py-2 rounded-lg',
isActive ? 'bg-blue-500 text-white' : 'bg-gray-100',
{ 'opacity-50': isLoading },
]">
数组写法
</div>
<!-- 方式四:计算属性(最佳实践,逻辑与模板分离) -->
<button :class="buttonClasses">提交</button>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
variant: String,
size: String,
disabled: Boolean,
})
// 将复杂的 class 逻辑提取到计算属性中
const buttonClasses = computed(() => {
const base = 'inline-flex items-center justify-center font-medium rounded-lg transition-colors'
const variantMap = {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-100 text-gray-700 hover:bg-gray-200',
}
const sizeMap = {
sm: 'text-sm px-3 py-1.5',
md: 'px-4 py-2',
lg: 'text-lg px-6 py-3',
}
return [
base,
variantMap[props.variant] ?? variantMap.primary,
sizeMap[props.size] ?? sizeMap.md,
props.disabled ? 'opacity-50 cursor-not-allowed pointer-events-none' : '',
]
})
</script>
生产环境:PurgeCSS / content 配置
Tailwind 通过扫描 content 配置的文件来确定哪些工具类被用到,未使用的类在生产构建时会被删除。
// tailwind.config.js
export default {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
// 如果有独立的工具函数生成类名,也要包含
"./src/utils/classNames.ts",
],
}
注意事项:
- 不要在代码中动态拼接类名(Tailwind 通过字符串匹配扫描,动态拼接会被删除)
- 动态类名必须完整写出:
// 错误:Tailwind 扫描不到 bg-blue-500、bg-red-500
const color = 'blue'
const className = `bg-${color}-500`
// 正确:完整的类名字符串
const classMap = {
blue: 'bg-blue-500',
red: 'bg-red-500',
green: 'bg-green-500',
}
const className = classMap[color]
- 如果使用了第三方组件库,将其路径也加入 content 扫描
clsx 和 tailwind-merge 工具
npm install clsx tailwind-merge
// utils/cn.ts
import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
// cn 函数:合并类名,自动解决 Tailwind 类冲突
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
<script setup>
import { cn } from '@/utils/cn'
// twMerge 会正确处理 Tailwind 类冲突
// 例如 'px-2 px-4' 会合并为 'px-4'(后者优先)
const classes = cn(
'px-4 py-2 rounded-lg', // 基础类
props.variant === 'primary' && 'bg-blue-500 text-white',
props.disabled && 'opacity-50',
props.className, // 外部传入的类(可覆盖内部类)
)
</script>
clsx 解决条件类名的可读性问题,tailwind-merge 解决 Tailwind 类名冲突(如同时有 px-2 和 px-4 时自动保留后者)。
100 个常用工具类速查表
布局类
| 类名 |
CSS 等价 |
用途 |
container |
max-width + 断点适配 |
内容区域容器 |
flex |
display: flex |
弹性布局 |
grid |
display: grid |
网格布局 |
hidden |
display: none |
隐藏元素 |
block |
display: block |
块级显示 |
inline-flex |
display: inline-flex |
行内弹性布局 |
relative |
position: relative |
相对定位 |
absolute |
position: absolute |
绝对定位 |
fixed |
position: fixed |
固定定位 |
sticky |
position: sticky |
粘性定位 |
inset-0 |
top/right/bottom/left: 0 |
覆盖父元素 |
overflow-hidden |
overflow: hidden |
裁剪溢出内容 |
overflow-auto |
overflow: auto |
按需滚动 |
z-10 |
z-index: 10 |
层级 |
truncate |
单行溢出省略 |
文字截断 |
Flexbox 类
| 类名 |
CSS 等价 |
用途 |
flex-col |
flex-direction: column |
垂直排列 |
flex-row |
flex-direction: row |
水平排列 |
flex-wrap |
flex-wrap: wrap |
允许换行 |
items-center |
align-items: center |
交叉轴居中 |
items-start |
align-items: flex-start |
交叉轴顶对齐 |
items-end |
align-items: flex-end |
交叉轴底对齐 |
justify-center |
justify-content: center |
主轴居中 |
justify-between |
justify-content: space-between |
两端对齐 |
justify-end |
justify-content: flex-end |
主轴末端 |
flex-1 |
flex: 1 1 0% |
均分剩余空间 |
shrink-0 |
flex-shrink: 0 |
禁止压缩 |
gap-4 |
gap: 1rem |
子元素间距 |
gap-2 |
gap: 0.5rem |
小间距 |
gap-6 |
gap: 1.5rem |
中等间距 |
gap-8 |
gap: 2rem |
大间距 |
Grid 类
| 类名 |
CSS 等价 |
用途 |
grid-cols-2 |
repeat(2, 1fr) |
两列网格 |
grid-cols-3 |
repeat(3, 1fr) |
三列网格 |
grid-cols-4 |
repeat(4, 1fr) |
四列网格 |
grid-cols-12 |
repeat(12, 1fr) |
十二列栅格 |
col-span-2 |
span 2 |
跨越两列 |
col-span-full |
1 / -1 |
跨越所有列 |
Spacing 类
| 类名 |
CSS 等价 |
用途 |
p-4 |
padding: 1rem |
四边内边距 |
px-4 |
padding-left/right: 1rem |
水平内边距 |
py-4 |
padding-top/bottom: 1rem |
垂直内边距 |
pt-4 |
padding-top: 1rem |
上内边距 |
pb-4 |
padding-bottom: 1rem |
下内边距 |
m-4 |
margin: 1rem |
四边外边距 |
mx-auto |
margin-left/right: auto |
水平居中 |
mt-4 |
margin-top: 1rem |
上外边距 |
mb-4 |
margin-bottom: 1rem |
下外边距 |
space-y-4 |
子元素垂直间距 1rem |
垂直间距(不含 gap) |
Sizing 类
| 类名 |
CSS 等价 |
用途 |
w-full |
width: 100% |
全宽 |
w-auto |
width: auto |
自动宽度 |
w-screen |
width: 100vw |
视口宽度 |
h-full |
height: 100% |
全高 |
h-screen |
height: 100vh |
视口高度 |
min-h-screen |
min-height: 100vh |
最小视口高度 |
max-w-7xl |
max-width: 80rem |
最大宽度限制 |
max-w-lg |
max-width: 32rem |
表单常用最大宽度 |
Typography 类
| 类名 |
CSS 等价 |
用途 |
text-sm |
font-size: 0.875rem |
小字体 |
text-base |
font-size: 1rem |
正文字体 |
text-lg |
font-size: 1.125rem |
略大字体 |
text-xl |
font-size: 1.25rem |
副标题 |
text-2xl |
font-size: 1.5rem |
标题 |
text-3xl |
font-size: 1.875rem |
大标题 |
font-normal |
font-weight: 400 |
正常字重 |
font-medium |
font-weight: 500 |
中等字重 |
font-semibold |
font-weight: 600 |
半粗 |
font-bold |
font-weight: 700 |
粗体 |
text-center |
text-align: center |
居中 |
text-left |
text-align: left |
左对齐 |
leading-relaxed |
line-height: 1.625 |
宽松行高 |
tracking-wide |
letter-spacing: 0.025em |
宽字距 |
uppercase |
text-transform: uppercase |
大写 |
颜色类
| 类名 |
用途 |
text-gray-500 |
辅助文字 |
text-gray-900 |
主要文字 |
text-white |
白色文字(用于深色背景) |
text-blue-600 |
链接/主色文字 |
bg-white |
白色背景 |
bg-gray-50 |
浅灰背景 |
bg-gray-100 |
次级背景 |
bg-blue-600 |
主色背景 |
Border 类
| 类名 |
CSS 等价 |
用途 |
border |
border-width: 1px solid |
默认边框 |
border-gray-200 |
边框颜色 |
浅灰边框 |
border-gray-300 |
边框颜色 |
中灰边框 |
rounded |
border-radius: 0.25rem |
小圆角 |
rounded-lg |
border-radius: 0.5rem |
中圆角 |
rounded-xl |
border-radius: 0.75rem |
大圆角 |
rounded-2xl |
border-radius: 1rem |
更大圆角 |
rounded-full |
border-radius: 9999px |
圆形 |
divide-y |
子元素间水平分割线 |
列表分割 |
divide-gray-200 |
分割线颜色 |
浅灰分割线 |
Effects 类
| 类名 |
CSS 等价 |
用途 |
shadow |
基础阴影 |
卡片阴影 |
shadow-md |
中阴影 |
浮层阴影 |
shadow-lg |
大阴影 |
弹窗阴影 |
ring-2 |
2px 轮廓 |
焦点样式 |
ring-blue-500 |
蓝色轮廓 |
主色焦点 |
opacity-50 |
opacity: 0.5 |
禁用态 |
Transition 类
| 类名 |
CSS 等价 |
用途 |
transition |
常用属性过渡 |
通用过渡 |
transition-colors |
颜色过渡 |
hover 颜色变化 |
transition-transform |
变换过渡 |
缩放/移动动画 |
duration-200 |
200ms 时长 |
快速交互 |
duration-300 |
300ms 时长 |
标准过渡 |
ease-in-out |
缓入缓出 |
通用缓动 |
hover:scale-105 |
悬停放大 5% |
卡片悬停效果 |
综合实战示例
响应式导航栏
<!-- 导航栏:移动端汉堡菜单 + 桌面端横排链接 -->
<nav class="bg-white border-b border-gray-200 sticky top-0 z-50">
<div class="container mx-auto px-4">
<div class="flex items-center justify-between h-16">
<!-- Logo 区域 -->
<div class="flex items-center gap-2">
<div class="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center">
<span class="text-white font-bold text-sm">T</span>
</div>
<span class="font-bold text-gray-900 text-lg">品牌名称</span>
</div>
<!-- 桌面端导航链接(移动端隐藏) -->
<div class="hidden md:flex items-center gap-1">
<a href="#" class="text-gray-600 hover:text-blue-600 hover:bg-blue-50 px-3 py-2 rounded-md text-sm font-medium transition-colors">
首页
</a>
<a href="#" class="text-gray-600 hover:text-blue-600 hover:bg-blue-50 px-3 py-2 rounded-md text-sm font-medium transition-colors">
产品
</a>
<a href="#" class="text-gray-600 hover:text-blue-600 hover:bg-blue-50 px-3 py-2 rounded-md text-sm font-medium transition-colors">
文档
</a>
<a href="#" class="text-gray-600 hover:text-blue-600 hover:bg-blue-50 px-3 py-2 rounded-md text-sm font-medium transition-colors">
关于
</a>
</div>
<!-- 右侧操作按钮 -->
<div class="flex items-center gap-3">
<button class="hidden md:inline-flex items-center px-3 py-1.5 text-sm text-gray-700 hover:bg-gray-100 rounded-md transition-colors">
登录
</button>
<button class="inline-flex items-center px-4 py-1.5 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-lg transition-colors">
注册
</button>
<!-- 移动端汉堡菜单按钮 -->
<button class="md:hidden p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-md transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
</div>
</div>
</nav>
响应式卡片列表
<!-- 卡片网格:移动端单列,平板两列,桌面三列 -->
<section class="py-12 bg-gray-50">
<div class="container mx-auto px-4">
<!-- 标题区域 -->
<div class="text-center mb-10">
<h2 class="text-3xl font-bold text-gray-900 mb-3">精选内容</h2>
<p class="text-gray-500 max-w-xl mx-auto text-lg">
浏览我们精心筛选的内容,发现更多可能
</p>
</div>
<!-- 卡片网格 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- 单个卡片 -->
<div class="group bg-white rounded-2xl overflow-hidden shadow hover:shadow-lg transition-shadow duration-300 cursor-pointer">
<!-- 卡片封面图(使用渐变色代替实际图片) -->
<div class="h-48 bg-gradient-to-br from-blue-400 to-indigo-600 relative overflow-hidden">
<!-- 标签 -->
<div class="absolute top-3 left-3">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-white/20 text-white backdrop-blur-sm">
技术
</span>
</div>
<!-- 悬停时显示的叠加层 -->
<div class="absolute inset-0 bg-black/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex items-center justify-center">
<span class="text-white font-medium text-sm">查看详情</span>
</div>
</div>
<!-- 卡片内容 -->
<div class="p-5">
<!-- 元信息 -->
<div class="flex items-center gap-2 text-xs text-gray-400 mb-3">
<span>2026-03-05</span>
<span>·</span>
<span>5 分钟阅读</span>
</div>
<!-- 标题(悬停变色) -->
<h3 class="font-semibold text-gray-900 text-lg leading-snug mb-2 group-hover:text-blue-600 transition-colors">
Tailwind CSS 实战技巧:构建现代化 UI 组件
</h3>
<!-- 摘要 -->
<p class="text-gray-500 text-sm leading-relaxed line-clamp-2">
本文介绍如何使用 Tailwind CSS 工具类快速搭建响应式用户界面,包含常用模式和最佳实践。
</p>
<!-- 底部:作者 + 操作 -->
<div class="flex items-center justify-between mt-4 pt-4 border-t border-gray-100">
<div class="flex items-center gap-2">
<div class="w-7 h-7 rounded-full bg-gradient-to-br from-purple-400 to-pink-400 flex items-center justify-center">
<span class="text-white text-xs font-medium">A</span>
</div>
<span class="text-sm text-gray-600">作者名</span>
</div>
<button class="text-blue-600 text-sm font-medium hover:text-blue-800 transition-colors">
阅读 →
</button>
</div>
</div>
</div>
<!-- 可以重复以上卡片结构 -->
</div>
<!-- 加载更多按钮 -->
<div class="text-center mt-10">
<button class="inline-flex items-center gap-2 px-6 py-3 border border-gray-300 text-gray-700 font-medium rounded-xl hover:bg-gray-50 hover:border-gray-400 transition-colors duration-200">
加载更多
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
</div>
</div>
</section>
Vue3 综合组件示例
<!-- components/FeatureCard.vue:带 hover 效果的功能介绍卡片 -->
<template>
<div
class="group relative flex flex-col p-6 bg-white rounded-2xl border border-gray-200 hover:border-blue-300 hover:shadow-lg transition-all duration-300 cursor-pointer"
:class="{ 'ring-2 ring-blue-500': isSelected }"
@click="$emit('select', feature.id)"
>
<!-- 图标区域 -->
<div
class="inline-flex items-center justify-center w-12 h-12 rounded-xl mb-4 transition-colors duration-300"
:class="iconBgClass"
>
<component :is="feature.icon" class="w-6 h-6" :class="iconColorClass" />
</div>
<!-- 标题 -->
<h3 class="text-lg font-semibold text-gray-900 mb-2 group-hover:text-blue-700 transition-colors">
{{ feature.title }}
</h3>
<!-- 描述 -->
<p class="text-gray-500 text-sm leading-relaxed flex-1">
{{ feature.description }}
</p>
<!-- 底部:了解更多链接 -->
<div class="flex items-center gap-1 mt-4 text-sm font-medium text-blue-600 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
<span>了解更多</span>
<svg class="w-4 h-4 transform group-hover:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</div>
<!-- 选中状态标记 -->
<div
v-if="isSelected"
class="absolute top-3 right-3 w-5 h-5 bg-blue-500 rounded-full flex items-center justify-center"
>
<svg class="w-3 h-3 text-white" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
</svg>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
feature: {
type: Object,
required: true,
// 期望结构:{ id, title, description, icon, color }
},
isSelected: {
type: Boolean,
default: false,
},
})
defineEmits(['select'])
// 根据传入的 color 属性动态生成 Tailwind 类
// 注意:必须写完整的类名字符串,不能动态拼接(如 `bg-${color}-100` 会被 PurgeCSS 删除)
const colorMap = {
blue: { bg: 'bg-blue-100', icon: 'text-blue-600' },
green: { bg: 'bg-green-100', icon: 'text-green-600' },
purple: { bg: 'bg-purple-100', icon: 'text-purple-600' },
orange: { bg: 'bg-orange-100', icon: 'text-orange-600' },
red: { bg: 'bg-red-100', icon: 'text-red-600' },
}
const iconBgClass = computed(() => colorMap[props.feature.color]?.bg ?? 'bg-gray-100')
const iconColorClass = computed(() => colorMap[props.feature.color]?.icon ?? 'text-gray-600')
</script>
<!-- 父组件:功能网格展示 -->
<template>
<section class="py-16">
<div class="container mx-auto px-4 max-w-6xl">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5">
<FeatureCard
v-for="feature in features"
:key="feature.id"
:feature="feature"
:is-selected="selectedId === feature.id"
@select="selectedId = $event"
/>
</div>
</div>
</section>
</template>
<script setup>
import { ref } from 'vue'
import FeatureCard from './FeatureCard.vue'
const selectedId = ref(null)
const features = [
{ id: 1, title: '快速开发', description: '工具类直接写在 HTML,无需在文件间来回切换。', color: 'blue' },
{ id: 2, title: '高度定制', description: '通过配置文件轻松扩展设计系统,保持全局一致性。', color: 'purple' },
{ id: 3, title: '生产就绪', description: 'PurgeCSS 自动移除未使用样式,包体积极小。', color: 'green' },
]
</script>
踩坑与注意事项
动态类名必须完整书写
Tailwind 通过静态分析扫描文件中的完整类名字符串。任何动态拼接都会导致生产构建中类名被删除。
// 错误:构建后 bg-blue-500 会消失
const color = 'blue'
el.className = `bg-${color}-500`
// 正确:完整写出所有可能的类名
const colorClasses = { blue: 'bg-blue-500', red: 'bg-red-500' }
el.className = colorClasses[color]
不要在 style 属性中使用 @apply
@apply 只能在 CSS 文件(.css/.scss/.vue 的 <style> 块)中使用,不能在行内 style 属性中使用。
优先级问题
Tailwind 工具类都在同一层级,如果两个类作用于同一属性,以 HTML 中最后声明的类为准(CSS 权重相同时,后定义的样式生效)。使用 tailwind-merge 可以避免此类冲突。
JIT 模式(Tailwind v3 默认开启)
Tailwind v3 默认使用 JIT(即时编译)模式,按需生成类,支持任意值语法(如 w-[200px])。不需要手动开启。
不要忘记 content 配置
如果新增了文件类型或目录,必须在 tailwind.config.js 的 content 中添加,否则其中使用的工具类会被清除。
与 CSS 模块的兼容性
在使用 CSS Modules 的项目中,如果需要同时使用 Tailwind,确保 Tailwind 的 CSS 文件在全局引入(不通过 CSS Modules 导入),避免类名作用域冲突。
最佳实践
正确配置 content 路径,确保生产包不遗漏类名:Tailwind 通过扫描源文件中的类名字符串来生成 CSS,任何未被 content 覆盖的文件中的类都会被清除。新增文件类型或目录后必须同步更新配置。
// tailwind.config.js
module.exports = {
content: [
'./src/**/*.{html,js,ts,jsx,tsx,vue}',
'./components/**/*.{js,ts,jsx,tsx}',
],
}
用 @layer components 提取重复样式,而非大量使用 @apply:对于跨多个组件重复的样式组合,定义在 @layer components 中,并赋予语义化类名。@apply 适合少量提取,大量使用会抵消 Tailwind 的维护优势。
@layer components {
.btn-primary {
@apply px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors;
}
.card {
@apply rounded-lg border border-gray-200 shadow-sm p-4;
}
}
使用 tailwind-merge 解决动态类名冲突:在组件接收外部 className 时,直接字符串拼接可能导致同属性两个类并存(后者不一定覆盖前者)。tailwind-merge 会智能去重。
import { twMerge } from 'tailwind-merge'
function Button({ className, ...props }) {
return (
<button
className={twMerge('px-4 py-2 bg-blue-500 text-white', className)}
{...props}
/>
)
}
// 调用时传入 bg-red-500,正确覆盖默认颜色
<Button className="bg-red-500" />
优先使用语义化任意值,避免魔法数字散布各处:Tailwind 的任意值语法(w-[200px])适合一次性特殊值,但若同一值在多处重复出现,应通过 theme.extend 定义为设计令牌。
// 将项目特有的间距/颜色加入主题
module.exports = {
theme: {
extend: {
colors: { brand: '#1a73e8' },
spacing: { sidebar: '240px' },
},
},
}
暗色模式用 class 策略,由 JS 控制切换:media 策略跟随系统,无法让用户手动切换。class 策略在 <html> 上添加/移除 dark 类,可实现用户偏好记忆。
// tailwind.config.js
module.exports = { darkMode: 'class' }
// 切换暗色模式
document.documentElement.classList.toggle('dark')
常见陷阱
陷阱:动态拼接类名被 Purge 清除
现象: 开发环境样式正常,生产构建后动态计算的类(如 text-${color}-500)不生效,元素无样式。
原因: Tailwind 通过静态扫描源码字符串收集类名,无法识别运行时拼接的字符串片段,导致这些类被 purge 排除出最终 CSS。
解决: 始终写完整类名,不要拆分。如需动态,用对象映射完整类名。
// 错误:拼接,会被 purge
const cls = `text-${color}-500`
// 正确:完整类名映射
const colorMap = {
red: 'text-red-500',
blue: 'text-blue-500',
}
const cls = colorMap[color]
陷阱:@apply 在 Scoped CSS / CSS Modules 中失效
现象: 在 Vue <style scoped> 或 CSS Modules 文件中使用 @apply 报错,或样式无效。
原因: @apply 依赖 PostCSS 的 Tailwind 插件处理,但 Scoped CSS 的 scope 属性会干扰生成的选择器,部分构建工具版本下存在兼容性问题。
解决: 将共享样式移到全局 CSS 文件(非 scoped/module)中定义 @layer components,或直接在模板中写工具类。
陷阱:响应式前缀方向理解错误
现象: 设置 md:hidden 期望在小屏隐藏,实际在大屏上才隐藏;布局在小屏上崩掉。
原因: Tailwind 的响应式前缀是移动优先的"最小宽度"断点,md:hidden 表示"在 md 及以上宽度隐藏",不是"在 md 以下隐藏"。
解决: 移动端样式写在无前缀的基础类上,大屏覆写用断点前缀。
<!-- 移动端显示,md 及以上隐藏 -->
<div class="block md:hidden">移动菜单</div>
<!-- 移动端隐藏,md 及以上显示 -->
<div class="hidden md:block">桌面导航</div>
参见