tailwind-4

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

When to Use

适用场景

Triggers: When styling with Tailwind, using className, conditional styles, or dark mode.
Load when: styling with Tailwind CSS 4, using className, implementing dark mode, or needing conditional styles.
触发场景:使用Tailwind进行样式开发、使用className、条件样式或暗黑模式时。
加载时机:使用Tailwind CSS 4开发样式、使用className、实现暗黑模式或需要条件样式时。

Critical Patterns

核心模式

Pattern 1: Semantic classes — never var() in className

模式1:语义化类——切勿在className中使用var()

typescript
// ✅ Use semantic classes
<div className="bg-primary text-white" />
<div className="border-border" />
<div className="text-foreground bg-background" />

// ❌ Never var() in className
<div className="bg-[var(--color-primary)]" />
<div className="text-[var(--foreground)]" />
typescript
// ✅ 使用语义化类
<div className="bg-primary text-white" />
<div className="border-border" />
<div className="text-foreground bg-background" />

// ❌ 切勿在className中使用var()
<div className="bg-[var(--color-primary)]" />
<div className="text-[var(--foreground)]" />

Pattern 2: Literal values over hex

模式2:使用字面量值而非十六进制色值

typescript
// ✅ Semantic
<p className="text-white bg-slate-900" />
<p className="text-gray-500" />

// ❌ Avoid hex in className when an equivalent exists
<p className="text-[#ffffff] bg-[#0f172a]" />
typescript
// ✅ 语义化写法
<p className="text-white bg-slate-900" />
<p className="text-gray-500" />

// ❌ 当有等效语义化类时,避免在className中使用十六进制色值
<p className="text-[#ffffff] bg-[#0f172a]" />

Pattern 3: cn() for conditional styles

模式3:使用cn()处理条件样式

typescript
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';

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

// ✅ Use cn() for conditionals and conflicts
<button
  className={cn(
    'px-4 py-2 rounded-md font-medium',
    variant === 'primary' && 'bg-primary text-white',
    variant === 'ghost' && 'bg-transparent hover:bg-accent',
    disabled && 'opacity-50 cursor-not-allowed',
    className // allows external override
  )}
/>

// ✅ Static classes — no cn() needed
<div className="flex items-center gap-4 p-6" />
typescript
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';

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

// ✅ 使用cn()处理条件样式和类冲突
<button
  className={cn(
    'px-4 py-2 rounded-md font-medium',
    variant === 'primary' && 'bg-primary text-white',
    variant === 'ghost' && 'bg-transparent hover:bg-accent',
    disabled && 'opacity-50 cursor-not-allowed',
    className // 支持外部覆盖
  )}
/>

// ✅ 静态类——无需使用cn()
<div className="flex items-center gap-4 p-6" />

Code Examples

代码示例

Variants with cva (class-variance-authority)

使用cva(class-variance-authority)实现多变体组件

typescript
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';

const buttonVariants = cva(
  'inline-flex items-center justify-center rounded-md font-medium transition-colors',
  {
    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',
        ghost: 'hover:bg-accent hover:text-accent-foreground',
      },
      size: {
        sm: 'h-8 px-3 text-sm',
        md: 'h-10 px-4',
        lg: 'h-12 px-6 text-lg',
      },
    },
    defaultVariants: {
      variant: 'default',
      size: 'md',
    },
  }
);

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

function Button({ variant, size, className, ...props }: ButtonProps) {
  return (
    <button
      className={cn(buttonVariants({ variant, size }), className)}
      {...props}
    />
  );
}
typescript
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';

const buttonVariants = cva(
  'inline-flex items-center justify-center rounded-md font-medium transition-colors',
  {
    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',
        ghost: 'hover:bg-accent hover:text-accent-foreground',
      },
      size: {
        sm: 'h-8 px-3 text-sm',
        md: 'h-10 px-4',
        lg: 'h-12 px-6 text-lg',
      },
    },
    defaultVariants: {
      variant: 'default',
      size: 'md',
    },
  }
);

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

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

Dark Mode

暗黑模式

typescript
// ✅ Dark mode with Tailwind classes
<div className="bg-white dark:bg-gray-900">
  <p className="text-gray-900 dark:text-gray-100">Content</p>
  <button className="bg-blue-600 dark:bg-blue-500 hover:bg-blue-700 dark:hover:bg-blue-400">
    Action
  </button>
</div>

// CSS config (tailwind.config.ts)
export default {
  darkMode: 'class', // or 'media'
  // ...
}
typescript
// ✅ 使用Tailwind类实现暗黑模式
<div className="bg-white dark:bg-gray-900">
  <p className="text-gray-900 dark:text-gray-100">内容</p>
  <button className="bg-blue-600 dark:bg-blue-500 hover:bg-blue-700 dark:hover:bg-blue-400">
    操作
  </button>
</div>

// CSS配置(tailwind.config.ts)
export default {
  darkMode: 'class', // 或 'media'
  // ...
}

Responsive Design

响应式设计

typescript
// Mobile-first with breakpoints
<div className="
  flex flex-col          // mobile: column
  md:flex-row            // tablet: row
  lg:grid lg:grid-cols-3 // desktop: 3-col grid
  gap-4
">
  <Card />
  <Card />
  <Card />
</div>
typescript
// 移动端优先的断点设计
<div className="
  flex flex-col          // 移动端:纵向布局
  md:flex-row            // 平板:横向布局
  lg:grid lg:grid-cols-3 // 桌面端:3列网格
  gap-4
">
  <Card />
  <Card />
  <Card />
</div>

Exception for Libraries (Recharts, etc.)

第三方库例外情况(如Recharts等)

typescript
// ✅ For libraries that don't support className, use CSS var constants
const CHART_COLORS = {
  primary: 'var(--color-primary)',
  secondary: 'var(--color-secondary)',
  muted: 'var(--color-muted)',
} as const;

<LineChart>
  <Line stroke={CHART_COLORS.primary} />
</LineChart>
typescript
// ✅ 对于不支持className的库,使用CSS变量常量
const CHART_COLORS = {
  primary: 'var(--color-primary)',
  secondary: 'var(--color-secondary)',
  muted: 'var(--color-muted)',
} as const;

<LineChart>
  <Line stroke={CHART_COLORS.primary} />
</LineChart>

Dynamic values at runtime

运行时动态值

typescript
// ✅ style prop for runtime calculations
<div style={{ width: `${percentage}%` }} className="bg-primary h-2 rounded" />

// ✅ CSS custom properties for theming
<div
  style={{ '--card-cols': columns } as React.CSSProperties}
  className="grid grid-cols-[repeat(var(--card-cols),1fr)]"
/>
typescript
// ✅ 使用style属性处理运行时计算
<div style={{ width: `${percentage}%` }} className="bg-primary h-2 rounded" />

// ✅ 使用CSS自定义属性实现主题定制
<div
  style={{ '--card-cols': columns } as React.CSSProperties}
  className="grid grid-cols-[repeat(var(--card-cols),1fr)]"
/>

Anti-Patterns

反模式

❌ var() in className

❌ 在className中使用var()

typescript
// ❌ Breaks Tailwind's optimizer
<div className="bg-[var(--primary)] text-[var(--fg)]" />

// ✅
<div className="bg-primary text-foreground" />
typescript
// ❌ 会破坏Tailwind的优化器
<div className="bg-[var(--primary)] text-[var(--fg)]" />

// ✅ 正确写法
<div className="bg-primary text-foreground" />

❌ String concatenation for conditionals

❌ 使用字符串拼接处理条件样式

typescript
// ❌ Can generate invalid classes
<div className={'text-sm ' + (active ? 'text-blue-600' : 'text-gray-500')} />

// ✅
<div className={cn('text-sm', active ? 'text-blue-600' : 'text-gray-500')} />
typescript
// ❌ 可能生成无效类名
<div className={'text-sm ' + (active ? 'text-blue-600' : 'text-gray-500')} />

// ✅ 正确写法
<div className={cn('text-sm', active ? 'text-blue-600' : 'text-gray-500')} />

❌ cn() for purely static classes

❌ 对纯静态类使用cn()

typescript
// ❌ Unnecessary
<div className={cn('flex items-center gap-4')} />

// ✅
<div className="flex items-center gap-4" />
typescript
// ❌ 无必要
<div className={cn('flex items-center gap-4')} />

// ✅ 正确写法
<div className="flex items-center gap-4" />

Quick Reference

速查表

TaskPattern
Semantic color
bg-primary
,
text-foreground
Conditional
cn('base', condition && 'variant')
Variants
cva('base', { variants: ... })
Dark mode
dark:bg-slate-900
Responsive
sm:
md:
lg:
xl:
Runtime value
style={{ width: \
${val}%` }}`
External overrideAccept and apply
className
prop with
cn()
任务实现模式
语义化颜色
bg-primary
,
text-foreground
条件样式
cn('base', condition && 'variant')
多变体组件
cva('base', { variants: ... })
暗黑模式
dark:bg-slate-900
响应式设计
sm:
md:
lg:
xl:
运行时动态值
style={{ width: \
${val}%` }}`
外部类覆盖通过cn()接收并应用
className
属性

Rules

规则

  • Use the
    cn()
    utility (clsx + tailwind-merge) for all conditional class composition; string concatenation for dynamic classes causes class conflicts that tailwind-merge resolves
  • Tailwind 4 uses CSS-first configuration (
    @theme
    in CSS) — never use
    tailwind.config.js
    theme extensions for new Tailwind 4 projects
  • Avoid
    @apply
    in component CSS files; Tailwind utility classes belong in the markup, not extracted into CSS rules
  • Component library classes (shadcn/ui, Radix) must not be overridden with Tailwind classes on the same element — extend via variants or wrapper elements
  • Dynamic class names must be complete strings (e.g.,
    'text-red-500'
    ), never constructed by string interpolation (e.g.,
    `text-${color}-500`
    ) — PurgeCSS/Tailwind cannot detect partial class names
  • 所有条件类组合都使用
    cn()
    工具函数(结合clsx + tailwind-merge);使用字符串拼接处理动态类会导致类冲突,而tailwind-merge可以解决这个问题
  • Tailwind 4采用CSS优先的配置方式(在CSS中使用
    @theme
    )——全新的Tailwind 4项目切勿使用
    tailwind.config.js
    扩展主题
  • 避免在组件CSS文件中使用
    @apply
    ;Tailwind工具类应直接写在标记中,而非提取到CSS规则里
  • 组件库类(如shadcn/ui、Radix)不能在同一元素上用Tailwind类覆盖——应通过变体或包装元素进行扩展
  • 动态类名必须是完整字符串(例如
    'text-red-500'
    ),切勿通过字符串插值构造(例如
    `text-${color}-500`
    )——PurgeCSS/Tailwind无法检测部分类名