附录 C:Tailwind CSS 速查表(Cheat Sheet)

常用组件模式(超简速查) Button(样式配方) Input Card Modal(Dialog Content) 附录 C 总结 本附录内容具有以下特点: * 覆盖 Tailwind 绝大部分使用场景 * 高可读性(按功能分类) * 不冗余(真正常用速查内容) * 与 shad...

本附录是 Tailwind CSS 的「速查手册 + 深度进阶」二合一文档。前半部分新增了五个深度技术章节,后半部分将原有 24 个分类速查手册折叠保留,随时可作为日常开发速查入口。


目录

  1. 设计系统原子化实践:从 Figma Token 到 tailwind.config.js 映射
  2. shadcn/ui 组件库原理:基于 Radix + Tailwind 的无头组件模式
  3. Tailwind 与 CSS-in-JS(styled-components/Emotion)的协作与取舍
  4. Tailwind 性能优化:JIT 模式原理、PurgeCSS 配置、Critical CSS 提取
  5. 实战:用 Tailwind 构建一套完整的 SaaS Dashboard UI
  6. 附录:24 分类速查表(折叠速查版)

一、设计系统原子化实践:从 Figma Token 到 tailwind.config.js 映射

现代 SaaS 产品的视觉一致性要求越来越高,直接从 Tailwind 默认配色出发生产代码往往导致品牌调性缺失。将设计稿中的 Token 体系无损映射到工程代码,是规模化团队必须解决的核心问题。Tailwind 的配置架构天然就是为「设计系统即代码」而生。

1.1 Figma 设计 Token 的结构化提取

设计团队在 Figma 中通常会定义一套规范化的 Token,常见层级分为三组:

  • 语义层(Semantic):color-text-primary、color-surface-default、spacing-md
  • 品牌层(Brand):color-brand-500、color-brand-600
  • 基础层(Primitive):十六进制色值、具体 rem 像素值

从 Figma 导出 Token 工具有多种选择,包括官方推出的 Tokens Studio 插件以及 Style Dictionary CLI。但无论使用何种工具,最终目标都是将这三层数据收敛为工程侧可消费的 CSS 变量或配置文件。

// style-dictionary.config.json —— 将 Figma Token 转为 CSS 变量
{
  "source": ["tokens/**/*.json"],
  "platforms": {
    "css": {
      "transformGroup": "css",
      "buildPath": "src/styles/",
      "files": [{
        "destination": "tokens.css",
        "format": "css/variables"
      }]
    }
  }
}
/* 生成的 tokens.css */
:root {
  --color-brand-50: #eff6ff;
  --color-brand-500: #3b82f6;
  --color-brand-600: #2563eb;
  --color-text-primary: #111827;
  --color-surface: #ffffff;
  --radius-card: 0.75rem;
  --shadow-card: 0 4px 12px rgba(0, 0, 0, 0.08);
}

1.2 tailwind.config.js 映射

在 Tailwind 配置中,使用 theme.extend 将 CSS 变量注册为 Tailwind 类名,保留全部默认主题的基础上增量叠加品牌规范:

// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './src/**/*.{js,ts,jsx,tsx,vue,html}',
    './index.html',
  ],
  theme: {
    extend: {
      colors: {
        brand: {
          50:  'var(--color-brand-50)',
          100: '#dbeafe',
          500: 'var(--color-brand-500)',
          600: 'var(--color-brand-600)',
          900: '#1e3a8a',
        },
        surface: {
          DEFAULT: 'var(--color-surface)',
          elevated: '#fafafa',
        },
        'text-primary': 'var(--color-text-primary)',
      },
      fontFamily: {
        sans: ['Inter', 'system-ui', 'sans-serif'],
        mono: ['JetBrains Mono', 'monospace'],
      },
      fontSize: {
        '2xs': ['0.625rem', { lineHeight: '0.875rem' }],
      },
      spacing: {
        4.5: '1.125rem',
        13: '3.25rem',
      },
      borderRadius: {
        card: 'var(--radius-card)',
      },
      boxShadow: {
        'card': 'var(--shadow-card)',
        'card-hover': '0 8px 24px rgba(0, 0, 0, 0.12)',
      },
      animation: {
        'fade-in': 'fadeIn 0.3s ease-out',
      },
      keyframes: {
        fadeIn: {
          '0%': { opacity: '0', transform: 'translateY(4px)' },
          '100%': { opacity: '1', transform: 'translateY(0)' },
        },
      },
    },
  },
  plugins: [
    require('@tailwindcss/typography'),
    require('@tailwindcss/forms'),
  ],
}

上述映射完成后,项目中可直接使用 text-brand-500,bg-brand-50,rounded-card 等类名,其值与设计稿 Token 一一对应。设计师更新 Figma Token 后只需重新生成 tokens.css,无需修改 Tailwind 配置文件或组件代码。

1.3 设计系统落地的五步流程

  1. 与设计团队协作,将 Figma 的颜色、字号、间距提取为结构化 Token。
  2. 在 tokens.css 中定义全部 CSS 变量,按语义命名(color-surface、color-text-primary 优于 color-white、color-black)。
  3. 在 tailwind.config.js 中将变量映射成 Tailwind 的 colors、fontSize、spacing 等扩展。
  4. 组件开发时全部使用 Tailwind 原子类,而非直接写 color: var(…),保持原子化一致性。
  5. 通过 附录 B:shadcn/ui 组件库实践 ,将 Token 落地为可复用的 Button、Card、Dialog 等组件。

这种原子化映射方式在大型 SaaS 项目中已被广泛验证,其核心优势在于消除了设计侧与工程侧之间的信息差,让视觉规范成为可执行的单向数据流。关于 Tailwind 中 CSS 变量与暗色模式结合的完整方案,还可以参考 附录 A:Tailwind 全类名一览表 中的深度章节。


二、shadcn/ui 组件库原理:基于 Radix + Tailwind 的无头组件模式

shadcn/ui 是近两年前端社区最具影响力的组件库之一。它并非传统意义上的「安装即用的 UI 包」,而是一种「可复制、可定制、无头化的组件架构」。其底层依赖 Radix UI 提供无障碍与行为逻辑,上层由 Tailwind 提供样式原子,开发者拿到的不是黑盒组件,而是可以随时修改的源码。

2.1 shadcn/ui 的设计哲学

shadcn/ui 的核心理念可以归纳为三点:

  • 拥有权:组件代码直接复制到项目 src/components/ui/ 目录,而非通过 npm 包引入。这意味着你可以任意修改样式和行为。
  • 无头化:交互逻辑、键盘导航、焦点管理、ARIA 属性全部由 Radix UI 的无头组件(Headless UI)提供,与样式完全解耦。
  • 原子化样式:所有组件样式均使用 Tailwind 原子类编写,通过 cn() 函数实现条件化类名组合。
// shadcn/ui Button 的典型结构
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/cn"

const buttonVariants = cva(
  "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground hover:bg-primary/90",
        destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
        outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "text-primary underline-offset-4 hover:underline",
      },
      size: {
        default: "h-10 px-4 py-2",
        sm: "h-9 rounded-md px-3",
        lg: "h-11 rounded-md px-8",
        icon: "h-10 w-10",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, asChild = false, ...props }, ref) => {
    const Comp = asChild ? Slot : "button"
    return (
      <Comp
        className={cn(buttonVariants({ variant, size, className }))}
        ref={ref}
        {...props}
      />
    )
  }
)
Button.displayName = "Button"
export { Button, buttonVariants }

2.2 class-variance-authority 与条件化样式

cva(class-variance-authority)是 shadcn/ui 样式系统的核心依赖。它将 Tailwind 类名按 variant 和 size 组织,使得组件的样式组合从「散落在各个 props 判断中的字符串拼接」变为「声明式的多维度配置表」。

// 使用 cva 定义弹窗 Dialog 的变体
const dialogVariants = cva(
  "fixed left-1/2 top-1/2 z-50 w-full max-w-lg -translate-x-1/2 -translate-y-1/2",
  {
    variants: {
      variant: {
        default: "rounded-xl border bg-background p-6 shadow-lg",
        alert: "rounded-lg border-destructive bg-background p-6 shadow-lg",
      },
    },
    defaultVariants: {
      variant: "default",
    },
  }
)

2.3 cn() 函数:类名合并与冲突解决

cn 是 clsx + tailwind-merge 的组合封装,clsx 处理条件化类名字符串,tailwind-merge 则在合并时自动消除冲突(例如 px-4 px-6 合并后只保留 px-6)。

// src/lib/cn.ts
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"

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

在 React Hooks 完全指南 中,我们探讨了组件状态管理;而在 shadcn/ui 体系中,组件的视觉状态(variant、size、disabled、loading)同样遵循声明式思维,二者在架构层面高度吻合。

2.4 Radix UI 提供的无障碍能力

Radix UI 是无头组件领域的标杆。以 Dialog 为例,Radix 自动处理以下交互逻辑:

  • 焦点陷阱(Focus Trap):弹窗打开时焦点锁定在内部,关闭后恢复到触发元素。
  • ESC 关闭:按下 Escape 键自动关闭弹窗。
  • 滚动锁定:底层页面禁止滚动,弹窗关闭后恢复。
  • ARIA 属性:自动注入 role=“dialog”、aria-modal=“true”、aria-labelledby 等属性。

开发者无需自行编写这些交互代码,只需专注于 Tailwind 样式的组合即可。关于更多 shadcn/ui 的组件实现细节,请参阅 附录 B:Tailwind + shadcn/ui 最佳实践 。


三、Tailwind 与 CSS-in-JS(styled-components/Emotion)的协作与取舍

在前端样式方案的发展史上,CSS-in-JS 与 Utility-First 代表了两种截然不同的思路。理解它们的适用边界,可以帮助团队在不同场景下做出更理性的技术选型。

3.1 styled-components 的典型工作流

styled-components 通过模板字符串将 CSS 直接写入 JavaScript 运行时,利用动态 props 生成不同样式:

// styled-components 示例
import styled, { css } from 'styled-components';

const Button = styled.button`
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: ${props => props.size === 'lg' ? '0.75rem 1.5rem' : '0.5rem 1rem'};
  border-radius: 0.375rem;
  font-weight: 500;
  transition: all 0.2s;

  ${props => props.variant === 'primary' && css`
    background-color: #3b82f6;
    color: white;
    &:hover { background-color: #2563eb; }
  `}

  ${props => props.variant === 'outline' && css`
    background-color: transparent;
    border: 1px solid #e5e7eb;
    &:hover { background-color: #f9fafb; }
  `}
`;

3.2 Tailwind 的 Utility-First 方案

同样的 Button,使用 Tailwind 可以写得更加紧凑:

<button class="inline-flex items-center justify-center rounded-md px-4 py-2 text-sm font-medium
  bg-blue-500 text-white hover:bg-blue-600 transition-colors">
  Primary
</button>

如果将这段逻辑封装成 React 组件,配合 cva 和 cn(),代码可读性与可维护性不输 styled-components:

import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/cn"

const btn = cva("inline-flex items-center justify-center rounded-md font-medium transition-colors", {
  variants: {
    variant: {
      primary: "bg-blue-500 text-white hover:bg-blue-600",
      outline: "bg-transparent border border-gray-200 hover:bg-gray-50",
    },
    size: {
      sm: "px-3 py-1.5 text-xs",
      md: "px-4 py-2 text-sm",
      lg: "px-6 py-3 text-base",
    },
  },
})

3.3 两种方案的核心对比

维度Tailwind Utility-Firststyled-components / Emotion
运行时开销编译期生成 CSS,零运行时运行时解析模板字符串,有额外开销
包体积仅打包使用的类名(JIT 模式)需注入完整样式运行时库
SSR 友好度极佳,CSS 文件独立输出需要额外配置水合和提取
动态主题CSS 变量即可需要 ThemeProvider 上下文
样式复用通过 @apply 或组件封装组件本身即复用单元
学习曲线需记忆大量类名需要掌握 CSS-in-JS 的语法与最佳实践

3.4 混合使用的最佳实践

在实际项目中,并非二选一的关系。Tailwind 负责全局布局、间距、排版、响应式变换等「重复但模式化的样式」;当遇到复杂的自定义动画、伪元素艺术效果、或需要基于运行时复杂计算的样式时,可以退回到 CSS-in-JS 或原生 CSS Modules:

/* src/styles/custom-animation.module.css */
@keyframes shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}

.shimmer {
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s infinite;
}
import styles from './custom-animation.module.css';
import { cn } from "@/lib/cn";

export function Skeleton() {
  return <div className={cn("h-4 w-full rounded", styles.shimmer)} />;
}

Vue 开发者可以参考 Vue Composition API 完全指南 中关于 scoped style 与原子类结合的讨论,两者的取舍逻辑与 React 生态一致。


四、Tailwind 性能优化:JIT 模式原理、PurgeCSS 配置、Critical CSS 提取

Tailwind 的性能神话并非凭空而来,而是建立在三个关键技术之上:JIT 即时编译引擎、PurgeCSS 未使用样式剔除、以及生产环境的 Critical CSS 提取策略。

4.1 JIT 模式原理

Tailwind v2.1 开始引入的 JIT(Just-In-Time)引擎彻底改变了 CSS 的生成方式。在 JIT 出现之前,Tailwind 需要扫描所有配置中的类名组合并预先生成完整的 CSS 文件,体积轻松达到数 MB。JIT 引擎只生成代码中实际使用到的类名,从零散文本解析出类名后即时编译为原生 CSS:

// tailwind.config.js
module.exports = {
  content: [
    './src/**/*.{html,js,ts,jsx,tsx,vue}',
    './pages/**/*.{html,js,ts,jsx,tsx}',
  ],
  // JIT 模式在 Tailwind v3 中已默认开启,无需额外配置
}

JIT 引擎的工作流程可以概括为三步:

  1. 扫描:遍历 content 中配置的全部文件,匹配所有符合 Tailwind 语法的类名字符串。
  2. 解析:将匹配到的每一组类名拆解为对应的原生 CSS 属性声明。
  3. 输出:只输出被使用到的 CSS 规则,未出现的类名不生成任何代码。

这使得最终生成的 CSS 文件体积通常控制在 10KB~30KB(gzip 后),相比传统方案实现了数量级的缩减。

4.2 PurgeCSS 配置与 content 扫描优化

content 数组决定了 JIT 引擎搜索类名的范围。配置过宽会延长构建时间,配置过窄则可能遗漏动态拼接的类名:

// tailwind.config.js —— content 优化示例
module.exports = {
  content: [
    // 精确命中源代码文件
    './src/**/*.{js,ts,jsx,tsx,vue}',
    './public/index.html',
    // 排除 node_modules 与构建产物
    '!./node_modules/**',
    '!./dist/**',
  ],
}

需要特别注意:任何形式的动态类名拼接都可能被 JIT 遗漏。例如 className={"text-" + color + "-500"} 这种写法无法被静态分析,解决方案是使用 safelist 保留这些类名:

module.exports = {
  safelist: [
    'text-red-500',
    'text-blue-500',
    'text-green-500',
    'bg-red-500',
    'bg-blue-500',
    'bg-green-500',
  ],
}

4.3 Critical CSS 提取

对于首屏渲染速度要求高的 Landing Page 或电商站点,仅压缩 CSS 体积还不够,需要进一步提取「首屏关键 CSS」并内联到 HTML head 中:

// critical.config.js —— 使用 critical npm 包
const critical = require('critical');

critical.generate({
  base: 'dist/',
  src: 'index.html',
  css: ['dist/assets/index-abc123.css'],
  dest: 'dist/index.html',
  width: 1300,
  height: 900,
  inline: true,
}).catch(err => {
  console.error('Critical CSS generation failed:', err);
});

更现代化的方案是借助 Next.js 的 optimizeFonts 与 CSS 内联能力,或者 Vite 的 vite-plugin-critical:

// vite.config.js
import critical from 'vite-plugin-critical';

export default {
  plugins: [
    critical({
      criticalUrl: 'http://localhost:5173',
      criticalBase: 'dist/',
    }),
  ],
};

关于 Next.js 中与 Tailwind 结合的性能优化策略,进一步请参阅 Next.js 速查表 中的渲染模式章节。在服务端渲染场景下,JIT 生成的精简 CSS 比动辄数百 KB 的 CSS-in-JS 运行时注入方案具有天然优势。

4.4 构建缓存与并行优化

大规模项目中,可通过以下手段进一步缩短 Tailwind 的编译耗时:

  • 使用 PostCSS cache 配置缓存中间产物,增量构建时只重新扫描变更文件。
  • 将 Tailwind 编译拆分为独立的 PostCSS 进程并行执行。
  • 在 CI/CD 流水线中复用 .postcss-cache/ 目录。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「frontend」更多文章

  1. GraphQL BFF 与微前端:多前端团队的 Schema 分片与协作模式
  2. 前端 GraphQL 客户端集成:Apollo Client、Relay 与 urql 选型
  3. 前端性能调试实战:从首屏加载到运行时瓶颈