Appearance
Tailwind CSS快速入门
概述
Tailwind CSS是一个功能优先(utility-first)的CSS框架,它提供了大量的原子化CSS类,让开发者无需编写自定义CSS即可快速构建现代化界面。与传统CSS框架不同,Tailwind不提供预设计的组件,而是提供构建块,让开发者自由组合创建独特的设计。本文将带你快速掌握Tailwind CSS在React项目中的使用。
安装和配置
在Vite项目中安装
bash
# 创建Vite + React项目
npm create vite@latest my-tailwind-app -- --template react
cd my-tailwind-app
# 安装Tailwind CSS及其依赖
npm install -D tailwindcss postcss autoprefixer
# 初始化Tailwind配置
npx tailwindcss init -pjavascript
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
// postcss.config.js
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}css
/* src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;jsx
// src/main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)在Next.js项目中安装
bash
# 创建Next.js项目
npx create-next-app@latest my-tailwind-app
# 在创建过程中选择:
# ✔ Would you like to use Tailwind CSS? Yes
# 或手动安装
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -pjavascript
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {},
},
plugins: [],
}css
/* app/globals.css 或 styles/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;在Create React App中安装
bash
# 创建CRA项目
npx create-react-app my-tailwind-app
cd my-tailwind-app
# 安装Tailwind及CRACO(CRA配置工具)
npm install -D tailwindcss postcss autoprefixer
npm install -D @craco/craco
# 初始化Tailwind
npx tailwindcss initjavascript
// craco.config.js
module.exports = {
style: {
postcss: {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
],
},
},
}
// package.json
{
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test"
}
}核心概念
Utility-First理念
jsx
// 传统CSS方式
// Button.css
.button {
padding: 0.5rem 1rem;
background-color: #3b82f6;
color: white;
border-radius: 0.25rem;
font-weight: 500;
}
.button:hover {
background-color: #2563eb;
}
// Button.jsx
import './Button.css';
function Button() {
return <button className="button">Click me</button>;
}
// Tailwind方式 - 直接使用工具类
function Button() {
return (
<button className="px-4 py-2 bg-blue-500 text-white rounded font-medium hover:bg-blue-600">
Click me
</button>
);
}响应式设计
jsx
// Tailwind的移动优先响应式断点
// sm: 640px
// md: 768px
// lg: 1024px
// xl: 1280px
// 2xl: 1536px
function ResponsiveComponent() {
return (
<div className="
w-full // 默认宽度100%
md:w-1/2 // 中等屏幕50%
lg:w-1/3 // 大屏幕33.33%
xl:w-1/4 // 超大屏幕25%
p-4 // 默认padding 1rem
md:p-6 // 中等屏幕padding 1.5rem
lg:p-8 // 大屏幕padding 2rem
">
<h1 className="
text-2xl // 默认字体大小
md:text-3xl // 中等屏幕
lg:text-4xl // 大屏幕
font-bold
">
Responsive Title
</h1>
</div>
);
}
// 网格布局响应式
function ResponsiveGrid() {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
{items.map(item => (
<div key={item.id} className="bg-white p-4 rounded shadow">
{item.content}
</div>
))}
</div>
);
}状态变体
jsx
// 悬停状态
function HoverExample() {
return (
<button className="
bg-blue-500
hover:bg-blue-600
text-white
px-4 py-2
rounded
transition-colors
">
Hover me
</button>
);
}
// 焦点状态
function FocusExample() {
return (
<input
type="text"
className="
border
border-gray-300
focus:border-blue-500
focus:ring
focus:ring-blue-200
rounded
px-3 py-2
outline-none
"
placeholder="Focus me"
/>
);
}
// 激活状态
function ActiveExample() {
return (
<button className="
bg-blue-500
hover:bg-blue-600
active:bg-blue-700
text-white
px-4 py-2
rounded
">
Click me
</button>
);
}
// 禁用状态
function DisabledExample({ disabled }) {
return (
<button
disabled={disabled}
className="
bg-blue-500
text-white
px-4 py-2
rounded
disabled:opacity-50
disabled:cursor-not-allowed
"
>
Submit
</button>
);
}
// 组合多个状态
function CombinedStates() {
return (
<button className="
bg-blue-500
text-white
px-4 py-2
rounded
hover:bg-blue-600
focus:outline-none
focus:ring-2
focus:ring-blue-300
active:bg-blue-700
disabled:opacity-50
transition-all
">
Button
</button>
);
}常用工具类
布局类
jsx
// Flexbox布局
function FlexLayout() {
return (
<>
{/* 水平居中 */}
<div className="flex justify-center items-center h-screen">
<div>Centered Content</div>
</div>
{/* 两端对齐 */}
<div className="flex justify-between items-center p-4">
<div>Left</div>
<div>Right</div>
</div>
{/* 垂直堆叠 */}
<div className="flex flex-col gap-4">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>
{/* 响应式Flex方向 */}
<div className="flex flex-col md:flex-row gap-4">
<div className="flex-1">Content 1</div>
<div className="flex-1">Content 2</div>
</div>
</>
);
}
// Grid布局
function GridLayout() {
return (
<>
{/* 基础Grid */}
<div className="grid grid-cols-3 gap-4">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
{/* 响应式Grid */}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
{items.map(item => <div key={item.id}>{item.content}</div>)}
</div>
{/* 自动填充 */}
<div className="grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-4">
{items.map(item => <div key={item.id}>{item.content}</div>)}
</div>
{/* Grid区域 */}
<div className="grid grid-cols-3 grid-rows-3 gap-4 h-screen">
<div className="col-span-3">Header</div>
<div className="row-span-2">Sidebar</div>
<div className="col-span-2 row-span-2">Main Content</div>
</div>
</>
);
}
// 定位
function PositionExample() {
return (
<>
{/* 相对定位 */}
<div className="relative">
<div className="absolute top-0 right-0">Badge</div>
</div>
{/* 固定定位 */}
<div className="fixed bottom-4 right-4 bg-blue-500 text-white p-4 rounded-full">
Scroll to Top
</div>
{/* 粘性定位 */}
<div className="sticky top-0 bg-white shadow z-10">
Sticky Header
</div>
</>
);
}间距类
jsx
function SpacingExample() {
return (
<>
{/* Padding */}
<div className="p-4">Padding all sides: 1rem</div>
<div className="px-4 py-2">Horizontal & Vertical padding</div>
<div className="pt-4 pr-3 pb-2 pl-1">Individual padding</div>
{/* Margin */}
<div className="m-4">Margin all sides: 1rem</div>
<div className="mx-auto">Centered with auto margin</div>
<div className="mt-4 mb-8">Top & Bottom margin</div>
{/* Gap (for Flex/Grid) */}
<div className="flex gap-4">
<div>Item 1</div>
<div>Item 2</div>
</div>
<div className="grid grid-cols-3 gap-x-4 gap-y-8">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>
{/* Space between */}
<div className="flex flex-col space-y-4">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>
</>
);
}颜色类
jsx
function ColorExample() {
return (
<>
{/* 文本颜色 */}
<p className="text-gray-900">Dark text</p>
<p className="text-blue-500">Blue text</p>
<p className="text-red-600">Red text</p>
{/* 背景颜色 */}
<div className="bg-white">White background</div>
<div className="bg-gray-100">Gray background</div>
<div className="bg-blue-500">Blue background</div>
{/* 边框颜色 */}
<div className="border border-gray-300">Gray border</div>
<div className="border-2 border-blue-500">Blue border</div>
{/* 渐变背景 */}
<div className="bg-gradient-to-r from-blue-500 to-purple-600">
Gradient background
</div>
<div className="bg-gradient-to-br from-pink-500 via-red-500 to-yellow-500">
Multi-color gradient
</div>
{/* 透明度 */}
<div className="bg-blue-500 bg-opacity-50">50% opacity</div>
<div className="text-gray-900 text-opacity-75">75% text opacity</div>
</>
);
}文字类
jsx
function TypographyExample() {
return (
<>
{/* 字体大小 */}
<p className="text-xs">Extra small</p>
<p className="text-sm">Small</p>
<p className="text-base">Base</p>
<p className="text-lg">Large</p>
<p className="text-xl">Extra large</p>
<p className="text-2xl">2X large</p>
<p className="text-4xl">4X large</p>
{/* 字重 */}
<p className="font-thin">Thin</p>
<p className="font-normal">Normal</p>
<p className="font-medium">Medium</p>
<p className="font-semibold">Semibold</p>
<p className="font-bold">Bold</p>
<p className="font-black">Black</p>
{/* 文本对齐 */}
<p className="text-left">Left aligned</p>
<p className="text-center">Center aligned</p>
<p className="text-right">Right aligned</p>
<p className="text-justify">Justified text</p>
{/* 行高 */}
<p className="leading-none">No line height</p>
<p className="leading-tight">Tight</p>
<p className="leading-normal">Normal</p>
<p className="leading-relaxed">Relaxed</p>
<p className="leading-loose">Loose</p>
{/* 文本装饰 */}
<p className="underline">Underlined</p>
<p className="line-through">Line through</p>
<p className="no-underline">No underline</p>
{/* 文本转换 */}
<p className="uppercase">UPPERCASE</p>
<p className="lowercase">lowercase</p>
<p className="capitalize">Capitalize Each Word</p>
{/* 文本截断 */}
<p className="truncate w-64">
This is a very long text that will be truncated with ellipsis
</p>
<p className="line-clamp-3">
This text will be limited to 3 lines and show ellipsis after that.
Lorem ipsum dolor sit amet consectetur adipisicing elit.
</p>
</>
);
}边框和圆角
jsx
function BorderExample() {
return (
<>
{/* 边框宽度 */}
<div className="border">1px border</div>
<div className="border-2">2px border</div>
<div className="border-4">4px border</div>
<div className="border-8">8px border</div>
{/* 单边边框 */}
<div className="border-t">Top border</div>
<div className="border-r">Right border</div>
<div className="border-b">Bottom border</div>
<div className="border-l">Left border</div>
{/* 圆角 */}
<div className="rounded">Small rounded</div>
<div className="rounded-md">Medium rounded</div>
<div className="rounded-lg">Large rounded</div>
<div className="rounded-xl">Extra large rounded</div>
<div className="rounded-full">Fully rounded</div>
{/* 单角圆角 */}
<div className="rounded-tl-lg">Top-left rounded</div>
<div className="rounded-tr-lg">Top-right rounded</div>
<div className="rounded-bl-lg">Bottom-left rounded</div>
<div className="rounded-br-lg">Bottom-right rounded</div>
{/* 边框样式 */}
<div className="border-solid">Solid border</div>
<div className="border-dashed">Dashed border</div>
<div className="border-dotted">Dotted border</div>
<div className="border-double">Double border</div>
</>
);
}阴影和效果
jsx
function EffectsExample() {
return (
<>
{/* 阴影 */}
<div className="shadow-sm">Small shadow</div>
<div className="shadow">Default shadow</div>
<div className="shadow-md">Medium shadow</div>
<div className="shadow-lg">Large shadow</div>
<div className="shadow-xl">Extra large shadow</div>
<div className="shadow-2xl">2X large shadow</div>
<div className="shadow-inner">Inner shadow</div>
{/* 透明度 */}
<div className="opacity-0">Invisible</div>
<div className="opacity-25">25% opacity</div>
<div className="opacity-50">50% opacity</div>
<div className="opacity-75">75% opacity</div>
<div className="opacity-100">Fully visible</div>
{/* 模糊 */}
<div className="blur-sm">Small blur</div>
<div className="blur">Default blur</div>
<div className="blur-lg">Large blur</div>
{/* 亮度 */}
<div className="brightness-50">50% brightness</div>
<div className="brightness-100">Normal brightness</div>
<div className="brightness-150">150% brightness</div>
{/* 对比度 */}
<div className="contrast-50">50% contrast</div>
<div className="contrast-100">Normal contrast</div>
<div className="contrast-150">150% contrast</div>
</>
);
}实战组件示例
按钮组件
jsx
// 基础按钮
function Button({ children, variant = 'primary', size = 'md', ...props }) {
const baseClasses = "font-medium rounded transition-colors duration-200";
const variantClasses = {
primary: "bg-blue-500 text-white hover:bg-blue-600 active:bg-blue-700",
secondary: "bg-gray-500 text-white hover:bg-gray-600 active:bg-gray-700",
outline: "border-2 border-blue-500 text-blue-500 hover:bg-blue-50 active:bg-blue-100",
ghost: "text-blue-500 hover:bg-blue-50 active:bg-blue-100",
danger: "bg-red-500 text-white hover:bg-red-600 active:bg-red-700"
};
const sizeClasses = {
sm: "px-3 py-1.5 text-sm",
md: "px-4 py-2 text-base",
lg: "px-6 py-3 text-lg"
};
const className = `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]}`;
return (
<button className={className} {...props}>
{children}
</button>
);
}
// 使用示例
function ButtonExamples() {
return (
<div className="flex flex-wrap gap-4">
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="danger">Danger</Button>
<Button variant="primary" size="sm">Small</Button>
<Button variant="primary" size="md">Medium</Button>
<Button variant="primary" size="lg">Large</Button>
</div>
);
}卡片组件
jsx
function Card({ image, title, description, tags, author, date }) {
return (
<div className="max-w-sm rounded-lg overflow-hidden shadow-lg hover:shadow-xl transition-shadow duration-300 bg-white">
{/* 图片 */}
{image && (
<div className="relative h-48 overflow-hidden">
<img
src={image}
alt={title}
className="w-full h-full object-cover hover:scale-105 transition-transform duration-300"
/>
</div>
)}
{/* 内容 */}
<div className="p-6">
{/* 标签 */}
{tags && tags.length > 0 && (
<div className="flex flex-wrap gap-2 mb-3">
{tags.map(tag => (
<span
key={tag}
className="px-2 py-1 bg-blue-100 text-blue-600 text-xs rounded-full"
>
{tag}
</span>
))}
</div>
)}
{/* 标题 */}
<h3 className="text-xl font-bold text-gray-900 mb-2 line-clamp-2 hover:text-blue-600 transition-colors">
{title}
</h3>
{/* 描述 */}
<p className="text-gray-600 text-sm line-clamp-3 mb-4">
{description}
</p>
{/* 作者信息 */}
{author && (
<div className="flex items-center justify-between pt-4 border-t border-gray-200">
<div className="flex items-center gap-3">
<img
src={author.avatar}
alt={author.name}
className="w-10 h-10 rounded-full"
/>
<div>
<p className="text-sm font-medium text-gray-900">{author.name}</p>
<p className="text-xs text-gray-500">{date}</p>
</div>
</div>
</div>
)}
</div>
</div>
);
}
// 使用示例
function CardExample() {
return (
<Card
image="https://example.com/image.jpg"
title="Understanding Tailwind CSS"
description="Learn how to use Tailwind CSS to build modern user interfaces quickly and efficiently."
tags={['CSS', 'React', 'Tutorial']}
author={{
name: 'John Doe',
avatar: 'https://example.com/avatar.jpg'
}}
date="2024-01-15"
/>
);
}表单组件
jsx
function FormInput({ label, type = 'text', error, ...props }) {
return (
<div className="mb-4">
{label && (
<label className="block text-sm font-medium text-gray-700 mb-2">
{label}
</label>
)}
<input
type={type}
className={`
w-full px-4 py-2 border rounded-lg
focus:outline-none focus:ring-2 transition-colors
${error
? 'border-red-500 focus:ring-red-200'
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-200'
}
`}
{...props}
/>
{error && (
<p className="mt-1 text-sm text-red-600">{error}</p>
)}
</div>
);
}
function FormTextarea({ label, error, rows = 4, ...props }) {
return (
<div className="mb-4">
{label && (
<label className="block text-sm font-medium text-gray-700 mb-2">
{label}
</label>
)}
<textarea
rows={rows}
className={`
w-full px-4 py-2 border rounded-lg resize-none
focus:outline-none focus:ring-2 transition-colors
${error
? 'border-red-500 focus:ring-red-200'
: 'border-gray-300 focus:border-blue-500 focus:ring-blue-200'
}
`}
{...props}
/>
{error && (
<p className="mt-1 text-sm text-red-600">{error}</p>
)}
</div>
);
}
function ContactForm() {
const [formData, setFormData] = React.useState({
name: '',
email: '',
message: ''
});
const [errors, setErrors] = React.useState({});
const handleSubmit = (e) => {
e.preventDefault();
// 表单验证和提交逻辑
};
return (
<form onSubmit={handleSubmit} className="max-w-md mx-auto p-6 bg-white rounded-lg shadow-md">
<h2 className="text-2xl font-bold text-gray-900 mb-6">Contact Us</h2>
<FormInput
label="Name"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
error={errors.name}
placeholder="Your name"
/>
<FormInput
label="Email"
type="email"
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
error={errors.email}
placeholder="your@email.com"
/>
<FormTextarea
label="Message"
value={formData.message}
onChange={(e) => setFormData({ ...formData, message: e.target.value })}
error={errors.message}
placeholder="Your message..."
/>
<button
type="submit"
className="w-full bg-blue-500 text-white py-2 px-4 rounded-lg hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-300 transition-colors"
>
Send Message
</button>
</form>
);
}导航栏组件
jsx
function Navbar() {
const [isOpen, setIsOpen] = React.useState(false);
return (
<nav className="bg-white shadow-md sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between h-16">
{/* Logo */}
<div className="flex items-center">
<span className="text-2xl font-bold text-blue-600">Logo</span>
</div>
{/* Desktop Menu */}
<div className="hidden md:flex items-center space-x-8">
<a href="#" className="text-gray-700 hover:text-blue-600 transition-colors">Home</a>
<a href="#" className="text-gray-700 hover:text-blue-600 transition-colors">About</a>
<a href="#" className="text-gray-700 hover:text-blue-600 transition-colors">Services</a>
<a href="#" className="text-gray-700 hover:text-blue-600 transition-colors">Contact</a>
<button className="bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600 transition-colors">
Sign Up
</button>
</div>
{/* Mobile Menu Button */}
<div className="md:hidden flex items-center">
<button
onClick={() => setIsOpen(!isOpen)}
className="text-gray-700 hover:text-blue-600 focus:outline-none"
>
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
{isOpen ? (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
) : (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
)}
</svg>
</button>
</div>
</div>
</div>
{/* Mobile Menu */}
{isOpen && (
<div className="md:hidden">
<div className="px-2 pt-2 pb-3 space-y-1 sm:px-3 bg-white shadow-lg">
<a href="#" className="block px-3 py-2 text-gray-700 hover:bg-blue-50 hover:text-blue-600 rounded-md transition-colors">
Home
</a>
<a href="#" className="block px-3 py-2 text-gray-700 hover:bg-blue-50 hover:text-blue-600 rounded-md transition-colors">
About
</a>
<a href="#" className="block px-3 py-2 text-gray-700 hover:bg-blue-50 hover:text-blue-600 rounded-md transition-colors">
Services
</a>
<a href="#" className="block px-3 py-2 text-gray-700 hover:bg-blue-50 hover:text-blue-600 rounded-md transition-colors">
Contact
</a>
<button className="w-full text-left px-3 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition-colors">
Sign Up
</button>
</div>
</div>
)}
</nav>
);
}暗黑模式
配置暗黑模式
javascript
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
darkMode: 'class', // 或 'media' 使用系统偏好
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}实现暗黑模式
jsx
// 使用class策略
function DarkModeExample() {
return (
<div className="bg-white dark:bg-gray-900">
<h1 className="text-gray-900 dark:text-white">
Title
</h1>
<p className="text-gray-600 dark:text-gray-300">
Description
</p>
<button className="bg-blue-500 dark:bg-blue-600 text-white">
Button
</button>
</div>
);
}
// 暗黑模式切换
function DarkModeToggle() {
const [darkMode, setDarkMode] = React.useState(false);
React.useEffect(() => {
if (darkMode) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}, [darkMode]);
return (
<button
onClick={() => setDarkMode(!darkMode)}
className="p-2 rounded-lg bg-gray-200 dark:bg-gray-700"
>
{darkMode ? '🌞' : '🌙'}
</button>
);
}
// 使用localStorage持久化
function useDarkMode() {
const [darkMode, setDarkMode] = React.useState(() => {
const saved = localStorage.getItem('darkMode');
return saved === 'true';
});
React.useEffect(() => {
localStorage.setItem('darkMode', darkMode);
if (darkMode) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}, [darkMode]);
return [darkMode, setDarkMode];
}
// 使用系统偏好
function useSystemDarkMode() {
const [darkMode, setDarkMode] = React.useState(() => {
return window.matchMedia('(prefers-color-scheme: dark)').matches;
});
React.useEffect(() => {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const handler = (e) => setDarkMode(e.matches);
mediaQuery.addEventListener('change', handler);
return () => mediaQuery.removeEventListener('change', handler);
}, []);
return darkMode;
}性能优化
生产环境优化
javascript
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
// 生产环境配置
...(process.env.NODE_ENV === 'production' ? {
// 移除未使用的样式
purge: {
enabled: true,
content: ['./src/**/*.{js,jsx,ts,tsx}']
}
} : {})
}使用JIT模式
javascript
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
mode: 'jit', // Just-In-Time模式
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}最佳实践
1. 组件抽象
jsx
// 将重复的类组合抽象为组件
// 不好
function BadExample() {
return (
<>
<button className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
Button 1
</button>
<button className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
Button 2
</button>
</>
);
}
// 好
function Button({ children, ...props }) {
return (
<button
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
{...props}
>
{children}
</button>
);
}
function GoodExample() {
return (
<>
<Button>Button 1</Button>
<Button>Button 2</Button>
</>
);
}2. 使用@apply提取重复样式
css
/* styles/components.css */
@layer components {
.btn-primary {
@apply px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors;
}
.card {
@apply bg-white rounded-lg shadow-md p-6;
}
.input-field {
@apply w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-200;
}
}
// 使用
function Component() {
return (
<div className="card">
<input className="input-field" />
<button className="btn-primary">Submit</button>
</div>
);
}3. 条件类名管理
jsx
// 使用clsx或classnames库
import clsx from 'clsx';
function Button({ variant, size, disabled, children }) {
return (
<button
className={clsx(
'font-medium rounded transition-colors',
{
'bg-blue-500 text-white hover:bg-blue-600': variant === 'primary',
'bg-gray-500 text-white hover:bg-gray-600': variant === 'secondary',
'px-3 py-1.5 text-sm': size === 'sm',
'px-4 py-2 text-base': size === 'md',
'px-6 py-3 text-lg': size === 'lg',
'opacity-50 cursor-not-allowed': disabled
}
)}
disabled={disabled}
>
{children}
</button>
);
}总结
Tailwind CSS快速入门要点:
- 安装配置:支持Vite、Next.js、CRA等主流工具
- 核心概念:Utility-first、响应式、状态变体
- 常用工具类:布局、间距、颜色、文字、边框等
- 实战组件:按钮、卡片、表单、导航栏
- 暗黑模式:class或media策略
- 性能优化:JIT模式、生产环境配置
- 最佳实践:组件抽象、@apply、条件类名
掌握这些基础知识,就能快速使用Tailwind CSS构建现代化界面。