Tailwind CSS 实战指南:从基础到高级技巧

掌握 Tailwind CSS 配置、响应式设计、自定义组件和性能优化

Tailwind CSS 实战指南:从基础到高级技巧

Tailwind CSS 改变了 CSS 的编写方式。本文深入探讨 Tailwind 的最佳实践和高级技巧。

Tailwind 核心概念

原子化 CSS

Tailwind 设计哲学:
┌─────────────────────────────────────────────────────┐
│                                                     │
│   传统 CSS                                          │
│   └── 编写语义化类名 → 定义样式规则                │
│                                                     │
│   Tailwind CSS                                      │
│   └── 组合原子类 → 直接在 HTML 中构建样式         │
│                                                     │
│   优势                                              │
│   ├── 无需命名纠结                                  │
│   ├── 样式即组件                                    │
│   ├── 一致的设计系统                                │
│   └── 更小的生产包体积                              │
│                                                     │
└─────────────────────────────────────────────────────┘

基础用法

<!-- 传统方式 -->
<div class="card">
  <h2 class="card-title">标题</h2>
  <p class="card-content">内容</p>
</div>

<!-- Tailwind 方式 -->
<div class="bg-white rounded-lg shadow-md p-6">
  <h2 class="text-xl font-bold text-gray-900 mb-2">标题</h2>
  <p class="text-gray-600">内容</p>
</div>

配置文件

tailwind.config.js

// tailwind.config.js
module.exports = {
  content: [
    './src/**/*.{js,ts,jsx,tsx,astro}',
    './components/**/*.{js,ts,jsx,tsx}',
  ],

  theme: {
    extend: {
      // 自定义颜色
      colors: {
        primary: {
          50: '#eff6ff',
          100: '#dbeafe',
          500: '#3b82f6',
          600: '#2563eb',
          700: '#1d4ed8',
        },
        brand: '#ff6b35',
      },

      // 自定义字体
      fontFamily: {
        sans: ['Inter', 'system-ui', 'sans-serif'],
        mono: ['JetBrains Mono', 'monospace'],
      },

      // 自定义间距
      spacing: {
        '18': '4.5rem',
        '88': '22rem',
        '128': '32rem',
      },

      // 自定义断点
      screens: {
        'xs': '475px',
        '3xl': '1920px',
      },

      // 自定义动画
      animation: {
        'fade-in': 'fadeIn 0.5s ease-out',
        'slide-up': 'slideUp 0.3s ease-out',
      },
      keyframes: {
        fadeIn: {
          '0%': { opacity: '0' },
          '100%': { opacity: '1' },
        },
        slideUp: {
          '0%': { transform: 'translateY(10px)', opacity: '0' },
          '100%': { transform: 'translateY(0)', opacity: '1' },
        },
      },
    },
  },

  plugins: [
    require('@tailwindcss/forms'),
    require('@tailwindcss/typography'),
    require('@tailwindcss/aspect-ratio'),
  ],
};

响应式设计

断点使用

<!-- 移动优先设计 -->
<div class="
  w-full          /* 默认:全宽 */
  md:w-1/2        /* 中等屏幕:半宽 */
  lg:w-1/3        /* 大屏幕:三分之一 */
  xl:w-1/4        /* 超大屏幕:四分之一 */
">
  响应式卡片
</div>

<!-- 响应式网格 -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
  <div>项目 1</div>
  <div>项目 2</div>
  <div>项目 3</div>
  <div>项目 4</div>
</div>

<!-- 响应式导航 -->
<nav class="flex flex-col md:flex-row md:items-center md:justify-between">
  <div class="text-xl font-bold">Logo</div>
  <div class="hidden md:flex space-x-4">
    <a href="#">首页</a>
    <a href="#">关于</a>
    <a href="#">联系</a>
  </div>
</nav>

Container 查询

<!-- 使用 @container -->
<div class="@container">
  <div class="@md:flex @md:items-center">
    <img class="w-full @md:w-48" src="..." alt="" />
    <div class="@md:ml-4">
      <h3 class="text-lg @lg:text-xl">标题</h3>
    </div>
  </div>
</div>

状态变体

交互状态

<!-- Hover, Focus, Active -->
<button class="
  bg-blue-500
  hover:bg-blue-600
  focus:ring-2
  focus:ring-blue-300
  active:bg-blue-700
  transition-colors
">
  按钮
</button>

<!-- Group Hover -->
<div class="group">
  <div class="group-hover:bg-gray-100 p-4">
    <h3 class="group-hover:text-blue-600">标题</h3>
    <p class="group-hover:text-gray-700">描述</p>
  </div>
</div>

<!-- Peer 状态 -->
<input type="checkbox" class="peer" />
<label class="peer-checked:text-blue-600">
  选中时变蓝
</label>

暗色模式

<!-- 系统偏好 -->
<div class="bg-white dark:bg-gray-900">
  <h1 class="text-gray-900 dark:text-white">标题</h1>
  <p class="text-gray-600 dark:text-gray-400">内容</p>
</div>

<!-- 手动切换 -->
<script>
  // tailwind.config.js: darkMode: 'class'
  function toggleDark() {
    document.documentElement.classList.toggle('dark');
  }
</script>

组件抽象

@apply 指令

/* styles/components.css */
@layer components {
  .btn {
    @apply px-4 py-2 rounded-lg font-medium transition-colors;
  }

  .btn-primary {
    @apply btn bg-blue-500 text-white hover:bg-blue-600;
  }

  .btn-secondary {
    @apply btn bg-gray-200 text-gray-800 hover:bg-gray-300;
  }

  .input {
    @apply w-full px-3 py-2 border border-gray-300 rounded-lg
           focus:ring-2 focus:ring-blue-500 focus:border-transparent;
  }

  .card {
    @apply bg-white rounded-xl shadow-lg p-6;
  }
}

React 组件

// components/Button.tsx
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';

const buttonVariants = cva(
  'inline-flex items-center justify-center rounded-lg font-medium transition-colors',
  {
    variants: {
      variant: {
        primary: 'bg-blue-500 text-white hover:bg-blue-600',
        secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
        outline: 'border border-gray-300 hover:bg-gray-50',
        ghost: 'hover:bg-gray-100',
      },
      size: {
        sm: 'px-3 py-1.5 text-sm',
        md: 'px-4 py-2',
        lg: 'px-6 py-3 text-lg',
      },
    },
    defaultVariants: {
      variant: 'primary',
      size: 'md',
    },
  }
);

interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {}

export function Button({ className, variant, size, ...props }: ButtonProps) {
  return (
    <button
      className={cn(buttonVariants({ variant, size }), className)}
      {...props}
    />
  );
}

// 使用
<Button variant="primary" size="lg">主要按钮</Button>
<Button variant="outline">边框按钮</Button>

cn 工具函数

// lib/utils.ts
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

// 使用示例
cn('px-4 py-2', isActive && 'bg-blue-500', className);
// 自动合并冲突的类名

布局模式

Flexbox

<!-- 水平居中 -->
<div class="flex justify-center">
  <div>居中内容</div>
</div>

<!-- 垂直居中 -->
<div class="flex items-center h-screen">
  <div>居中内容</div>
</div>

<!-- 完全居中 -->
<div class="flex items-center justify-center h-screen">
  <div>居中内容</div>
</div>

<!-- Space Between -->
<div class="flex justify-between items-center">
  <div>左侧</div>
  <div>右侧</div>
</div>

Grid

<!-- 基础网格 -->
<div class="grid grid-cols-3 gap-4">
  <div>1</div>
  <div>2</div>
  <div>3</div>
</div>

<!-- 自适应网格 -->
<div class="grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-4">
  <div>卡片 1</div>
  <div>卡片 2</div>
  <div>卡片 3</div>
</div>

<!-- 复杂布局 -->
<div class="grid grid-cols-12 gap-4">
  <div class="col-span-12 lg:col-span-8">主内容</div>
  <div class="col-span-12 lg:col-span-4">侧边栏</div>
</div>

动画效果

<!-- 过渡 -->
<button class="
  bg-blue-500
  hover:bg-blue-600
  transition-all
  duration-300
  ease-in-out
  hover:scale-105
">
  悬停放大
</button>

<!-- 自定义动画 -->
<div class="animate-fade-in">
  淡入元素
</div>

<div class="animate-pulse">
  脉冲效果
</div>

<div class="animate-spin">
  旋转加载
</div>

<!-- 条件动画 -->
<div class="
  opacity-0
  translate-y-4
  transition-all
  duration-500
  data-[visible=true]:opacity-100
  data-[visible=true]:translate-y-0
">
  滚动显示
</div>

性能优化

生产构建

// tailwind.config.js
module.exports = {
  content: [
    // 确保只扫描需要的文件
    './src/**/*.{js,ts,jsx,tsx}',
  ],
  // 生产环境自动 Tree Shaking
};

最小化类名

<!-- 避免重复 -->
<div class="p-4 m-4">
  <!-- 使用 space 替代多个 margin -->
  <div class="space-y-4">
    <div>项目 1</div>
    <div>项目 2</div>
  </div>
</div>

<!-- 使用 divide 替代边框 -->
<ul class="divide-y divide-gray-200">
  <li class="py-4">项目 1</li>
  <li class="py-4">项目 2</li>
</ul>

最佳实践总结

Tailwind CSS 最佳实践:
┌─────────────────────────────────────────────────────┐
│   设计系统                                          │
│   ├── 在 config 中定义设计 Token                  │
│   ├── 使用一致的间距和颜色                          │
│   ├── 创建可复用的组件                              │
│   └── 使用 CVA 管理变体                            │
│                                                     │
│   代码组织                                          │
│   ├── 按逻辑分组类名                                │
│   ├── 使用 @apply 抽象常用模式                     │
│   ├── 配合 cn() 合并类名                           │
│   └── 组件化替代过长类名                            │
│                                                     │
│   性能优化                                          │
│   ├── 正确配置 content 路径                        │
│   ├── 生产环境自动 purge                           │
│   ├── 避免动态类名拼接                              │
│   └── 使用 JIT 模式                                │
│                                                     │
│   响应式设计                                        │
│   ├── 移动优先                                      │
│   ├── 合理使用断点                                  │
│   ├── Container 查询                               │
│   └── 暗色模式支持                                  │
└─────────────────────────────────────────────────────┘
场景推荐做法
按钮组件CVA + cn()
表单样式@tailwindcss/forms
文章排版@tailwindcss/typography
暗色模式dark: 前缀

Tailwind 让样式成为组件的一部分,写 CSS 从此不再纠结。