Tailwind CSS 项目实践:团队使用规范
Tailwind CSS 项目实践:团队使用规范
团队规范框架
在没有规范的情况下,团队使用 Tailwind CSS 经常会遇到以下问题:样式不一致、代码重复、维护困难、性能问题。
所以我们的规范主要包含以下几个方面:
- 统一的 tailwind.config.js 配置
- class 组合和组件命名规范
- 样式复用和抽象层级
- 文件结构和样式管理
- lint 规则和 code review 标准
- 打包和 purge(自动移除未使用 CSS 类) 最佳实践
1. 配置标准化
统一的设计 tokens
首先建立团队共用的tailwind.config.js
配置文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// tailwind.config.js
module.exports = {
content: [
"./src/**/*.{js,ts,jsx,tsx,vue,html}",
"./components/**/*.{js,ts,jsx,tsx,vue}",
],
theme: {
extend: {
// 品牌色彩系统
colors: {
primary: {
50: "#eff6ff",
100: "#dbeafe",
500: "#3b82f6",
600: "#2563eb",
700: "#1d4ed8",
900: "#1e3a8a",
},
gray: {
50: "#f9fafb",
100: "#f3f4f6",
200: "#e5e7eb",
500: "#6b7280",
700: "#374151",
900: "#111827",
},
},
// 统一的字体系统
fontSize: {
xs: ["12px", { lineHeight: "16px" }],
sm: ["14px", { lineHeight: "20px" }],
base: ["16px", { lineHeight: "24px" }],
lg: ["18px", { lineHeight: "28px" }],
xl: ["20px", { lineHeight: "28px" }],
"2xl": ["24px", { lineHeight: "32px" }],
},
// 间距系统
spacing: {
18: "4.5rem",
72: "18rem",
84: "21rem",
96: "24rem",
},
// 屏幕尺寸
screens: {
xs: "475px",
sm: "640px",
md: "768px",
lg: "1024px",
xl: "1280px",
"2xl": "1536px",
},
},
},
plugins: [
require("@tailwindcss/forms"),
require("@tailwindcss/typography"),
require("@tailwindcss/aspect-ratio"),
],
};
团队配置管理策略
- 版本控制:将配置文件纳入 Git 管理,确保团队同步
- 配置继承:不同项目可以基于基础配置进行扩展
- 定期 review:每月 review 配置文件,删除不用的 tokens
2. 命名约定和组织规范
Class 组合命名规范
建立清晰的 class 组合命名约定:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ❌ 不推荐:混乱的class排序
<div className="text-white bg-blue-500 p-4 rounded-lg shadow-md hover:bg-blue-600 flex items-center">
// ✅ 推荐:按功能模块分组
<div className="
// Layout
flex items-center
// Spacing
p-4
// Background & Border
bg-blue-500 rounded-lg
// Typography
text-white
// Effects
shadow-md
// Interactions
hover:bg-blue-600
">
可以使用 Prettier 的 Tailwind CSS 插件。
组件级别的样式抽象
对于重复使用的样式组合,我们采用三层抽象策略:
第一层:原子类直接使用
1
2
// 简单、一次性的样式
<span className="text-sm text-gray-500">辅助文本</span>
第二层:样式组合抽象
1
2
3
4
5
6
7
8
9
10
11
12
// utils/styles.js
export const buttonStyles = {
base: "px-4 py-2 font-medium rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2",
primary: "bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500",
secondary: "bg-gray-200 text-gray-900 hover:bg-gray-300 focus:ring-gray-500",
danger: "bg-red-600 text-white hover:bg-red-700 focus:ring-red-500",
}
// 使用方式
<button className={`${buttonStyles.base} ${buttonStyles.primary}`}>
主要按钮
</button>
第三层:组件封装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// components/Button.jsx
import { buttonStyles } from "../utils/styles";
export const Button = ({
variant = "primary",
size = "md",
children,
...props
}) => {
const sizeClasses = {
sm: "px-3 py-1.5 text-sm",
md: "px-4 py-2",
lg: "px-6 py-3 text-lg",
};
return (
<button
className={`
${buttonStyles.base}
${buttonStyles[variant]}
${sizeClasses[size]}
`}
{...props}
>
{children}
</button>
);
};
3. 代码组织和文件结构
推荐的项目结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
src/
├── styles/
│ ├── globals.css # 全局样式和Tailwind导入
│ ├── components.css # 组件级别的@apply样式
│ └── utilities.css # 自定义工具类
├── utils/
│ ├── styles.js # 样式组合工具
│ └── cn.js # className合并工具
├── components/
│ ├── ui/ # 基础UI组件
│ │ ├── Button.jsx
│ │ ├── Input.jsx
│ │ └── Modal.jsx
│ └── business/ # 业务组件
└── pages/ # 页面组件
样式管理最佳实践
1. 全局样式管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* styles/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* 全局基础样式 */
@layer base {
html {
@apply scroll-smooth;
}
body {
@apply bg-gray-50 text-gray-900 antialiased;
}
}
/* 组件样式 */
@layer components {
.btn-primary {
@apply px-4 py-2 bg-blue-600 text-white rounded-md font-medium;
@apply hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500;
@apply transition-colors duration-200;
}
}
2. className 合并工具
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// utils/cn.js
import { clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs) {
return twMerge(clsx(inputs))
}
// 使用示例
<div className={cn(
"base-styles",
isActive && "active-styles",
className // 允许外部覆盖
)}>
4. 质量保证
ESLint 和 Prettier 配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// .eslintrc.js
module.exports = {
extends: [
// ... 其他配置
],
plugins: ["tailwindcss"],
rules: {
// Tailwind特定规则
"tailwindcss/classnames-order": "warn",
"tailwindcss/no-custom-classname": "warn",
"tailwindcss/no-contradicting-classname": "error",
},
settings: {
tailwindcss: {
config: "tailwind.config.js",
},
},
};
代码审查检查清单
我们制定了专门的 Tailwind CSS 代码审查清单:
设计一致性
- 是否使用了统一的颜色 token
- 字体大小和行高是否符合设计系统
- 间距是否使用标准的 spacing scale
代码质量
- class 名是否按照约定顺序排列
- 是否有不必要的样式重复
- 复杂的样式组合是否应该抽象为组件
性能考虑
- 是否使用了不必要的复杂选择器
- 响应式设计是否合理
- 是否有 unused 的样式类
自动化工具集成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// package.json
{
"scripts": {
"lint:css": "stylelint '**/*.{css,scss}' --fix",
"lint:tw": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
"build:css": "tailwindcss -i ./src/styles/globals.css -o ./dist/output.css --watch"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
"*.{css,scss}": ["stylelint --fix"]
}
}
5. 性能优化实践
Purge 配置优化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// tailwind.config.js
module.exports = {
content: [
// 精确指定文件路径,避免扫描不必要的文件
"./src/pages/**/*.{js,ts,jsx,tsx}",
"./src/components/**/*.{js,ts,jsx,tsx}",
"./src/utils/**/*.{js,ts}",
// 如果使用动态class名,添加白名单
"./src/styles/safelist.txt",
],
// 生产环境配置
...(process.env.NODE_ENV === "production" && {
purge: {
options: {
safelist: [
// 动态生成的class名
/^bg-/,
/^text-/,
// 第三方库的class名
"swiper-slide",
"swiper-pagination",
],
},
},
}),
};
打包分析和监控
1
2
3
4
5
6
7
8
9
10
11
12
// 添加bundle分析
const BundleAnalyzerPlugin =
require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: "static",
openAnalyzer: false,
}),
],
};
常见问题和解决方案
Q1: 长串 class 名影响可读性怎么办?
解决方案:
1
2
3
4
5
6
7
8
9
10
11
// 使用模板字符串分行
const cardClasses = `
bg-white rounded-lg shadow-md
p-6 space-y-4
hover:shadow-lg transition-shadow
border border-gray-200
`
<div className={cardClasses}>
{/* 内容 */}
</div>
Q2: 动态样式如何处理?
解决方案:
1
2
3
4
5
6
7
8
// 使用条件渲染和样式映射
const statusStyles = {
success: 'bg-green-100 text-green-800 border-green-200',
warning: 'bg-yellow-100 text-yellow-800 border-yellow-200',
error: 'bg-red-100 text-red-800 border-red-200'
}
<div className={`base-styles ${statusStyles[status]}`}>
Q3: 如何处理复杂的响应式设计?
解决方案:
1
2
3
4
5
6
7
8
9
10
11
12
13
// 使用组件封装复杂响应式逻辑
const ResponsiveGrid = ({ children }) => (
<div
className="
grid grid-cols-1 gap-4
sm:grid-cols-2 sm:gap-6
lg:grid-cols-3 lg:gap-8
xl:grid-cols-4
"
>
{children}
</div>
);
总结与展望
通过建立完善的 Tailwind CSS 团队规范,大幅提升开发效率和代码质量,更重要的是建立了团队协作的共同语言。这套规范可以根据项目需求和技术发展持续优化。
This post is licensed under CC BY 4.0 by the author.