
Tailwind CSS:构建可复用组件
配置与设计令牌
// tailwind.config.js
module.exports = {
content: ['./src/**/*.{ts,tsx,html}'],
theme: {
extend: {
colors: {
brand: {
50: '#eff6ff',
100: '#dbeafe',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
900: '#1e3a8a',
},
danger: '#ef4444',
success: '#22c55e',
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
mono: ['JetBrains Mono', 'monospace'],
},
spacing: {
18: '4.5rem',
88: '22rem',
},
animation: {
'slide-in': 'slideIn 0.2s ease-out',
},
keyframes: {
slideIn: {
'0%': { transform: 'translateY(-10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
},
},
},
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
],
};

使用 cva 实现组件变体
import { cva, type VariantProps } from 'class-variance-authority';
const button = cva(
// 基础样式
'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
primary: 'bg-brand-600 text-white hover:bg-brand-700 focus-visible:ring-brand-500',
secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200',
outline: 'border border-gray-300 bg-transparent hover:bg-gray-50',
ghost: 'hover:bg-gray-100',
danger: 'bg-danger text-white hover:bg-red-600',
},
size: {
sm: 'h-8 px-3 text-sm',
md: 'h-10 px-4',
lg: 'h-12 px-6 text-lg',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'primary',
size: 'md',
},
}
);
interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof button> {
loading?: boolean;
}
export function Button({ variant, size, className, loading, children, ...props }: ButtonProps) {
return (
<button
className={button({ variant, size, className })}
disabled={loading || props.disabled}
{...props}
>
{loading && <Spinner className="mr-2 h-4 w-4" />}
{children}
</button>
);
}

暗色模式
// tailwind.config.js
module.exports = {
darkMode: 'class', // 或 'media' 以跟随系统偏好
};
// 主题切换
import { useState, useEffect } from 'react';
function useTheme() {
const [theme, setTheme] = useState<'light' | 'dark'>(() => {
if (typeof window === 'undefined') return 'light';
return (localStorage.getItem('theme') as 'light' | 'dark') ?? 'light';
});
useEffect(() => {
document.documentElement.classList.toggle('dark', theme === 'dark');
localStorage.setItem('theme', theme);
}, [theme]);
return { theme, toggle: () => setTheme(t => t === 'light' ? 'dark' : 'light') };
}
// 在组件中使用
<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">Hello</h1>
</div>

响应式设计模式
<!-- 移动优先的响应式网格 -->
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<!-- 卡片 -->
</div>
<!-- 响应式侧边栏布局 -->
<div class="flex flex-col lg:flex-row gap-6">
<aside class="w-full lg:w-64 shrink-0">侧边栏</aside>
<main class="flex-1 min-w-0">主内容</main>
</div>
<!-- 在断点处显示/隐藏 -->
<nav class="hidden md:flex gap-4">桌面导航</nav>
<button class="md:hidden">汉堡菜单</button>
Typography 插件
<!-- 使用 prose 类处理富文本内容 -->
<article class="prose prose-gray dark:prose-invert max-w-none lg:prose-xl">
<!-- Markdown 渲染的 HTML -->
</article>
Tailwind 的实用优先方法消除了命名摩擦,并实现了快速原型设计。