第 5 章:配置与定制
9/1/25About 7 min
第 5 章:配置与定制
学习目标
- 深入理解 tailwind.config.js 配置文件
- 学会扩展默认主题和创建设计系统
- 掌握插件系统的使用和开发
- 了解生产环境优化和文件体积控制
tailwind.config.js 深度解析
完整配置结构
// tailwind.config.js
const defaultTheme = require('tailwindcss/defaultTheme')
module.exports = {
// 内容源配置
content: [
'./src/**/*.{html,js,ts,jsx,tsx,vue,svelte}',
'./public/index.html',
// 包含第三方库
'./node_modules/flowbite/**/*.js'
],
// 预设配置
presets: [
// require('./my-preset.js')
],
// 暗色模式
darkMode: 'class', // 'media' | 'class' | false
// 主题配置
theme: {
// 完全替换默认配置
screens: {
'tablet': '640px',
'laptop': '1024px',
'desktop': '1280px',
},
// 扩展默认配置
extend: {
// 在这里添加自定义配置
}
},
// 变体配置
variants: {
extend: {
// 扩展变体
}
},
// 插件
plugins: [
// 官方插件
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
require('@tailwindcss/aspect-ratio'),
// 自定义插件
require('./plugins/custom-plugin'),
],
// 核心插件配置
corePlugins: {
// 禁用特定核心插件
float: false,
objectFit: false,
},
// 前缀
prefix: 'tw-',
// 重要性
important: false, // true | '#app'
// 分隔符
separator: ':',
// CSS-in-JS 配置
corePlugins: {
preflight: false, // 禁用基础样式重置
}
}
内容配置最佳实践
// 推荐的内容配置
module.exports = {
content: {
files: [
'./src/**/*.{html,js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
'./pages/**/*.{js,ts,jsx,tsx}',
'./app/**/*.{js,ts,jsx,tsx}',
],
// 动态内容提取
extract: {
js: (content) => {
// 从 JS 文件中提取类名
return content.match(/[A-Za-z0-9_-]+/g) || []
}
},
// 转换内容
transform: {
md: (content) => {
// 处理 Markdown 文件
return content.replace(/<!--.*?-->/gs, '')
}
}
},
// 安全列表 - 确保这些类不被清除
safelist: [
'bg-red-500',
'text-3xl',
'lg:text-4xl',
// 动态生成的类名
/^bg-(red|green|blue)-(100|200|300|400|500)$/,
// 复杂匹配
{
pattern: /bg-(red|green|blue)-(100|200|300|400|500)/,
variants: ['lg', 'hover', 'focus', 'lg:hover'],
},
]
}
主题定制
颜色系统定制
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
// 添加品牌色彩
primary: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6', // 主色调
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
},
// 语义化颜色
success: {
50: '#f0fdf4',
500: '#22c55e',
900: '#14532d',
},
danger: {
50: '#fef2f2',
500: '#ef4444',
900: '#7f1d1d',
},
warning: {
50: '#fefce8',
500: '#eab308',
900: '#713f12',
},
// 中性色
gray: {
50: '#fafafa',
100: '#f5f5f5',
200: '#e5e5e5',
300: '#d4d4d4',
400: '#a3a3a3',
500: '#737373',
600: '#525252',
700: '#404040',
800: '#262626',
900: '#171717',
},
// 单一颜色值
'brand-blue': '#0066cc',
'accent': 'rgb(255, 107, 107)',
'custom-gray': 'hsl(210, 20%, 50%)',
}
}
}
}
字体系统定制
module.exports = {
theme: {
extend: {
fontFamily: {
// 添加自定义字体
'sans': ['Inter', 'system-ui', ...defaultTheme.fontFamily.sans],
'serif': ['Merriweather', ...defaultTheme.fontFamily.serif],
'mono': ['JetBrains Mono', ...defaultTheme.fontFamily.mono],
'display': ['Oswald', 'Georgia', 'serif'],
'body': ['Open Sans', 'system-ui', 'sans-serif'],
'chinese': [
'PingFang SC',
'Hiragino Sans GB',
'Microsoft YaHei',
'WenQuanYi Micro Hei',
'sans-serif'
],
},
fontSize: {
// 自定义字体大小
'xs': ['0.75rem', { lineHeight: '1rem' }],
'tiny': '0.625rem',
'7xl': '4.5rem',
'8xl': '6rem',
'9xl': '8rem',
},
fontWeight: {
'extra-light': 100,
'medium': 500,
'extra-bold': 800,
'black': 900,
},
letterSpacing: {
'tighter': '-0.05em',
'wider': '0.05em',
'widest': '0.25em',
},
lineHeight: {
'extra-tight': '1.1',
'extra-loose': '2.5',
}
}
}
}
间距和尺寸定制
module.exports = {
theme: {
extend: {
spacing: {
// 添加自定义间距
'0.5': '0.125rem', // 2px
'1.5': '0.375rem', // 6px
'2.5': '0.625rem', // 10px
'3.5': '0.875rem', // 14px
'15': '3.75rem', // 60px
'18': '4.5rem', // 72px
'72': '18rem', // 288px
'84': '21rem', // 336px
'96': '24rem', // 384px
'128': '32rem', // 512px
// 百分比间距
'1/7': '14.2857143%',
'2/7': '28.5714286%',
'3/7': '42.8571429%',
'4/7': '57.1428571%',
'5/7': '71.4285714%',
'6/7': '85.7142857%',
// 视窗单位
'screen-1/4': '25vh',
'screen-1/2': '50vh',
'screen-3/4': '75vh',
},
maxWidth: {
'8xl': '88rem',
'9xl': '96rem',
'1/4': '25%',
'1/2': '50%',
'3/4': '75%',
},
minHeight: {
'0': '0',
'1/4': '25%',
'1/2': '50%',
'3/4': '75%',
'full': '100%',
'screen': '100vh',
'screen-1/2': '50vh',
}
}
}
}
断点定制
module.exports = {
theme: {
// 完全自定义断点
screens: {
'xs': '475px',
'sm': '640px',
'md': '768px',
'lg': '1024px',
'xl': '1280px',
'2xl': '1536px',
'3xl': '1920px',
// 命名断点
'mobile': '640px',
'tablet': '768px',
'laptop': '1024px',
'desktop': '1280px',
'wide': '1536px',
// 最大宽度断点
'max-sm': {'max': '639px'},
'max-md': {'max': '767px'},
'max-lg': {'max': '1023px'},
// 范围断点
'sm-md': {'min': '640px', 'max': '767px'},
'lg-xl': {'min': '1024px', 'max': '1279px'},
// 高度断点
'tall': {'raw': '(min-height: 800px)'},
'short': {'raw': '(max-height: 600px)'},
// 设备特定断点
'print': {'raw': 'print'},
'landscape': {'raw': '(orientation: landscape)'},
'portrait': {'raw': '(orientation: portrait)'},
}
}
}
插件系统
官方插件
# 安装官方插件
npm install -D @tailwindcss/forms
npm install -D @tailwindcss/typography
npm install -D @tailwindcss/aspect-ratio
npm install -D @tailwindcss/line-clamp
npm install -D @tailwindcss/container-queries
// tailwind.config.js
module.exports = {
plugins: [
// 表单样式增强
require('@tailwindcss/forms')({
strategy: 'class', // 'base' | 'class'
}),
// 排版样式
require('@tailwindcss/typography')({
modifiers: ['sm', 'lg', 'xl', '2xl'],
}),
// 宽高比
require('@tailwindcss/aspect-ratio'),
// 文本截断
require('@tailwindcss/line-clamp'),
// 容器查询
require('@tailwindcss/container-queries'),
],
}
自定义工具类插件
// plugins/utilities.js
const plugin = require('tailwindcss/plugin')
module.exports = plugin(function({ addUtilities, theme, variants }) {
const utilities = {
// 文字阴影
'.text-shadow': {
textShadow: '2px 2px 4px rgba(0,0,0,0.1)',
},
'.text-shadow-md': {
textShadow: '4px 4px 6px rgba(0,0,0,0.1)',
},
'.text-shadow-lg': {
textShadow: '6px 6px 8px rgba(0,0,0,0.15)',
},
'.text-shadow-none': {
textShadow: 'none',
},
// 3D 变换
'.preserve-3d': {
transformStyle: 'preserve-3d',
},
'.perspective': {
perspective: '1000px',
},
'.backface-hidden': {
backfaceVisibility: 'hidden',
},
// 滚动行为
'.scroll-smooth': {
scrollBehavior: 'smooth',
},
'.scroll-auto': {
scrollBehavior: 'auto',
},
// 自定义光标
'.cursor-grab': {
cursor: 'grab',
},
'.cursor-grabbing': {
cursor: 'grabbing',
},
}
addUtilities(utilities, ['responsive', 'hover', 'focus'])
})
自定义组件插件
// plugins/components.js
const plugin = require('tailwindcss/plugin')
module.exports = plugin(function({ addComponents, theme }) {
addComponents({
// 按钮组件
'.btn': {
padding: `${theme('spacing.2')} ${theme('spacing.4')}`,
borderRadius: theme('borderRadius.md'),
fontWeight: theme('fontWeight.medium'),
fontSize: theme('fontSize.sm'),
lineHeight: theme('lineHeight.5'),
transition: 'all 0.15s ease-in-out',
cursor: 'pointer',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
border: '1px solid transparent',
textDecoration: 'none',
userSelect: 'none',
'&:focus': {
outline: '2px solid transparent',
boxShadow: `0 0 0 2px ${theme('colors.blue.500')}`,
},
'&:disabled': {
opacity: '0.5',
cursor: 'not-allowed',
},
},
'.btn-primary': {
backgroundColor: theme('colors.blue.500'),
color: theme('colors.white'),
'&:hover:not(:disabled)': {
backgroundColor: theme('colors.blue.600'),
},
},
'.btn-secondary': {
backgroundColor: theme('colors.gray.200'),
color: theme('colors.gray.900'),
'&:hover:not(:disabled)': {
backgroundColor: theme('colors.gray.300'),
},
},
'.btn-outline': {
backgroundColor: 'transparent',
borderColor: theme('colors.gray.300'),
color: theme('colors.gray.700'),
'&:hover:not(:disabled)': {
backgroundColor: theme('colors.gray.50'),
borderColor: theme('colors.gray.400'),
},
},
// 表单组件
'.form-input': {
width: '100%',
padding: `${theme('spacing.2')} ${theme('spacing.3')}`,
backgroundColor: theme('colors.white'),
border: `1px solid ${theme('colors.gray.300')}`,
borderRadius: theme('borderRadius.md'),
fontSize: theme('fontSize.sm'),
lineHeight: theme('lineHeight.5'),
transition: 'border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out',
'&:focus': {
outline: 'none',
borderColor: theme('colors.blue.500'),
boxShadow: `0 0 0 1px ${theme('colors.blue.500')}`,
},
'&::placeholder': {
color: theme('colors.gray.400'),
},
},
// 卡片组件
'.card': {
backgroundColor: theme('colors.white'),
borderRadius: theme('borderRadius.lg'),
boxShadow: theme('boxShadow.md'),
border: `1px solid ${theme('colors.gray.200')}`,
overflow: 'hidden',
},
'.card-header': {
padding: theme('spacing.4'),
borderBottom: `1px solid ${theme('colors.gray.200')}`,
backgroundColor: theme('colors.gray.50'),
},
'.card-body': {
padding: theme('spacing.4'),
},
'.card-footer': {
padding: theme('spacing.4'),
backgroundColor: theme('colors.gray.50'),
borderTop: `1px solid ${theme('colors.gray.200')}`,
},
})
})
动态工具类插件
// plugins/dynamic-utilities.js
const plugin = require('tailwindcss/plugin')
module.exports = plugin(function({ matchUtilities, theme }) {
// 动态间距工具类
matchUtilities(
{
'auto-fit': (value) => ({
gridTemplateColumns: `repeat(auto-fit, minmax(${value}, 1fr))`,
}),
'auto-fill': (value) => ({
gridTemplateColumns: `repeat(auto-fill, minmax(${value}, 1fr))`,
}),
},
{
values: theme('width'),
respectPrefix: false,
respectImportant: false,
}
)
// 动态阴影工具类
matchUtilities(
{
'text-shadow': (value) => ({
textShadow: value,
}),
},
{
values: {
sm: '1px 1px 2px rgba(0, 0, 0, 0.1)',
default: '2px 2px 4px rgba(0, 0, 0, 0.1)',
md: '4px 4px 8px rgba(0, 0, 0, 0.12)',
lg: '8px 8px 16px rgba(0, 0, 0, 0.15)',
xl: '12px 12px 24px rgba(0, 0, 0, 0.2)',
},
}
)
})
暗色模式支持
配置暗色模式
// tailwind.config.js
module.exports = {
darkMode: 'class', // 'media' | 'class' | ['class', '[data-mode="dark"]']
theme: {
extend: {
colors: {
// 暗色模式专用颜色
dark: {
50: '#f9fafb',
100: '#f3f4f6',
200: '#e5e7eb',
300: '#d1d5db',
400: '#9ca3af',
500: '#6b7280',
600: '#4b5563',
700: '#374151',
800: '#1f2937',
900: '#111827',
}
}
}
}
}
使用暗色模式
<!-- HTML 结构 -->
<html class="dark">
<body class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
<div class="bg-white dark:bg-gray-800 shadow-lg">
<h1 class="text-gray-900 dark:text-white">标题</h1>
<p class="text-gray-600 dark:text-gray-300">内容</p>
<button class="bg-blue-500 dark:bg-blue-600 text-white hover:bg-blue-600 dark:hover:bg-blue-700">
按钮
</button>
</div>
</body>
</html>
/* CSS 自定义属性支持暗色模式 */
@layer base {
:root {
--color-primary: 59 130 246; /* blue-500 */
--color-background: 255 255 255; /* white */
--color-text: 17 24 39; /* gray-900 */
}
.dark {
--color-primary: 37 99 235; /* blue-600 */
--color-background: 17 24 39; /* gray-900 */
--color-text: 249 250 251; /* gray-50 */
}
}
.custom-bg {
background-color: rgb(var(--color-background));
}
.custom-text {
color: rgb(var(--color-text));
}
生产环境优化
PurgeCSS 配置
// tailwind.config.js
module.exports = {
content: {
files: [
'./src/**/*.{html,js,ts,jsx,tsx}',
],
options: {
// 白名单 - 永不删除的类
safelist: [
'bg-red-500',
'text-center',
/^bg-(red|green|blue)-(100|200|300|400|500|600|700|800|900)$/,
{
pattern: /^(bg|text|border)-(red|green|blue)-(100|500|900)$/,
variants: ['hover', 'focus', 'lg:hover'],
},
],
// 阻止删除列表
blocklist: [
'container',
'collapsible',
],
// 默认提取器
defaultExtractor: content => content.match(/[\w-/:]+/g) || [],
// 自定义提取器
extractors: [
{
extractor: content => {
// 从 Vue 组件中提取类名
return content.match(/[A-Za-z0-9_-]+/g) || []
},
extensions: ['vue']
}
]
}
}
}
文件体积优化
// postcss.config.js - 生产环境配置
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
...(process.env.NODE_ENV === 'production'
? [
require('cssnano')({
preset: ['default', {
discardComments: { removeAll: true },
normalizeWhitespace: false,
}]
})
]
: []
),
],
}
构建脚本优化
{
"scripts": {
"build:css": "tailwindcss -i ./src/input.css -o ./dist/output.css",
"build:css:watch": "tailwindcss -i ./src/input.css -o ./dist/output.css --watch",
"build:css:prod": "NODE_ENV=production tailwindcss -i ./src/input.css -o ./dist/output.css --minify",
"analyze:css": "tailwindcss -i ./src/input.css -o ./dist/output.css --content './src/**/*.{html,js}' --verbose"
}
}
小结
通过本章学习,你应该掌握了:
- 配置系统:全面了解 tailwind.config.js 的各项配置
- 主题定制:创建符合品牌的颜色、字体和间距系统
- 插件开发:编写自定义工具类和组件插件
- 暗色模式:实现完整的暗色主题支持
- 性能优化:生产环境的文件体积控制策略
这些配置和定制技能让你能够将 Tailwind CSS 打造成完全符合项目需求的CSS框架。