dev-tailwind-expert

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Tailwind CSS Expert

Tailwind CSS 专家指南

Workflow

工作流程

1. Initialiser le projet

1. 初始化项目

bash
undefined
bash
undefined

Tailwind v4 (2025+) — recommandé pour tous les nouveaux projets

Tailwind v4 (2025+) — 所有新项目推荐使用

npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite

OU avec PostCSS

或使用PostCSS

npm install tailwindcss postcss autoprefixer npx tailwindcss init -p

**Critère de décision v3 vs v4 :**
- v4 : projet neuf, Vite/Remix/Next 15+, CSS-first config (`@theme`), performances build x2
- v3 : projet existant ou contrainte de compatibilité ecosystème (plugins tiers non migrés)
npm install tailwindcss postcss autoprefixer npx tailwindcss init -p

**v3与v4版本选择标准:**
- v4:新项目、Vite/Remix/Next 15+、CSS优先配置(`@theme`)、构建性能提升2倍
- v3:已有项目或受生态兼容性限制(第三方插件未迁移)

2. Configurer
tailwind.config.ts

2. 配置
tailwind.config.ts

ts
// tailwind.config.ts (v3)
import type { Config } from 'tailwindcss'

export default {
  content: [
    './src/**/*.{ts,tsx,html}',
    './node_modules/@acme/ui/dist/**/*.js', // libs externes si nécessaire
  ],
  darkMode: 'class', // ou 'media'
  theme: {
    extend: {
      colors: {
        brand: {
          50: '#f0f9ff',
          500: '#0ea5e9',
          900: '#0c4a6e',
        },
        destructive: 'hsl(var(--color-destructive) / <alpha-value>)',
      },
      fontFamily: {
        sans: ['Inter Variable', 'system-ui', 'sans-serif'],
      },
      screens: {
        xs: '480px', // breakpoint custom avant sm
      },
    },
  },
  plugins: [
    require('@tailwindcss/forms'),
    require('@tailwindcss/typography'),
  ],
} satisfies Config
En v4 (CSS-first) : la config se fait dans le CSS directement :
css
/* app.css */
@import "tailwindcss";
@theme {
  --color-brand-500: oklch(62% 0.19 240);
  --font-sans: "Inter Variable", system-ui, sans-serif;
}
ts
// tailwind.config.ts (v3)
import type { Config } from 'tailwindcss'

export default {
  content: [
    './src/**/*.{ts,tsx,html}',
    './node_modules/@acme/ui/dist/**/*.js', // 如有需要可包含外部库
  ],
  darkMode: 'class', // 或 'media'
  theme: {
    extend: {
      colors: {
        brand: {
          50: '#f0f9ff',
          500: '#0ea5e9',
          900: '#0c4a6e',
        },
        destructive: 'hsl(var(--color-destructive) / <alpha-value>)',
      },
      fontFamily: {
        sans: ['Inter Variable', 'system-ui', 'sans-serif'],
      },
      screens: {
        xs: '480px', // 在sm之前自定义断点
      },
    },
  },
  plugins: [
    require('@tailwindcss/forms'),
    require('@tailwindcss/typography'),
  ],
} satisfies Config
v4版本(CSS优先): 配置直接在CSS中完成:
css
/* app.css */
@import "tailwindcss";
@theme {
  --color-brand-500: oklch(62% 0.19 240);
  --font-sans: "Inter Variable", system-ui, sans-serif;
}

3. Construire les design tokens

3. 构建设计令牌

Principe : définir une fois, utiliser partout via des variables CSS ou tokens Tailwind.
ts
// Couleurs sémantiques → mappe les tokens sur des rôles
colors: {
  primary: 'hsl(var(--primary) / <alpha-value>)',
  'primary-foreground': 'hsl(var(--primary-foreground) / <alpha-value>)',
}
css
/* globals.css */
:root {
  --primary: 221 83% 53%;
  --primary-foreground: 0 0% 100%;
}
.dark {
  --primary: 217 91% 70%;
}
原则:定义一次,通过CSS变量或Tailwind令牌全局复用。
ts
// 语义化颜色 → 将令牌映射到角色
colors: {
  primary: 'hsl(var(--primary) / <alpha-value>)',
  'primary-foreground': 'hsl(var(--primary-foreground) / <alpha-value>)',
}
css
/* globals.css */
:root {
  --primary: 221 83% 53%;
  --primary-foreground: 0 0% 100%;
}
.dark {
  --primary: 217 91% 70%;
}

4. Implémenter les composants

4. 实现组件

Mobile-first systématique :
html
<!-- Mauvais : pas de base mobile -->
<div class="lg:flex lg:gap-8">...</div>

<!-- Bon : mobile d'abord, puis breakpoints -->
<div class="flex flex-col gap-4 md:flex-row md:gap-8">...</div>
Composition conditionnelle avec
cn
(clsx + tailwind-merge) :
ts
import { clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'

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

// Utilisation
<button className={cn(
  'px-4 py-2 rounded-md font-medium transition-colors',
  variant === 'primary' && 'bg-brand-500 text-white hover:bg-brand-600',
  variant === 'ghost'   && 'bg-transparent text-brand-500 hover:bg-brand-50',
  disabled && 'opacity-50 cursor-not-allowed',
)}>
Groupes et peer :
html
<!-- group-hover : effet parent → enfant -->
<div class="group rounded-lg border p-4 hover:border-brand-500">
  <p class="text-gray-500 group-hover:text-brand-500">Texte</p>
</div>

<!-- peer : état sibling input → label -->
<input id="email" class="peer" required />
<p class="hidden peer-invalid:block text-red-500 text-sm">Email invalide</p>
严格遵循移动端优先:
html
<!-- 错误:无移动端基础样式 -->
<div class="lg:flex lg:gap-8">...</div>

<!-- 正确:先移动端,再断点样式 -->
<div class="flex flex-col gap-4 md:flex-row md:gap-8">...</div>
使用
cn
(clsx + tailwind-merge)实现条件组合:
ts
import { clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'

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

// 使用示例
<button className={cn(
  'px-4 py-2 rounded-md font-medium transition-colors',
  variant === 'primary' && 'bg-brand-500 text-white hover:bg-brand-600',
  variant === 'ghost'   && 'bg-transparent text-brand-500 hover:bg-brand-50',
  disabled && 'opacity-50 cursor-not-allowed',
)}>
Group与Peer选择器:
html
<!-- group-hover:父元素触发子元素效果 -->
<div class="group rounded-lg border p-4 hover:border-brand-500">
  <p class="text-gray-500 group-hover:text-brand-500">文本</p>
</div>

<!-- peer:兄弟input元素状态触发label样式 -->
<input id="email" class="peer" required />
<p class="hidden peer-invalid:block text-red-500 text-sm">邮箱无效</p>

5. Dark mode

5. 深色模式

ts
// tailwind.config.ts
darkMode: 'class' // toggle via JS — meilleure UX
ts
// Hook de toggle (React)
function useDarkMode() {
  const toggle = () => document.documentElement.classList.toggle('dark')
  return toggle
}
html
<!-- Classes dark: sur chaque token de couleur -->
<div class="bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
ts
// tailwind.config.ts
darkMode: 'class' // 通过JS切换 — 用户体验更佳
ts
// 切换钩子(React)
function useDarkMode() {
  const toggle = () => document.documentElement.classList.toggle('dark')
  return toggle
}
html
<!-- 为每个颜色令牌添加dark:前缀 -->
<div class="bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">

6. Plugins custom

6. 自定义插件

ts
// Plugin utilitaires — ex: text-balance manquant en v3
const plugin = require('tailwindcss/plugin')

module.exports = {
  plugins: [
    plugin(({ addUtilities, matchUtilities, theme }) => {
      // Utilitaire statique
      addUtilities({
        '.text-balance': { 'text-wrap': 'balance' },
        '.scrollbar-none': { 'scrollbar-width': 'none' },
      })
      // Utilitaire dynamique avec valeur config
      matchUtilities(
        { 'grid-cols-auto': (value) => ({ gridTemplateColumns: `repeat(auto-fill, minmax(${value}, 1fr))` }) },
        { values: theme('spacing') }
      )
    }),
  ],
}
// Usage : <div class="grid-cols-auto-64">
ts
// 工具类插件 — 例:v3中缺少的text-balance
const plugin = require('tailwindcss/plugin')

module.exports = {
  plugins: [
    plugin(({ addUtilities, matchUtilities, theme }) => {
      // 静态工具类
      addUtilities({
        '.text-balance': { 'text-wrap': 'balance' },
        '.scrollbar-none': { 'scrollbar-width': 'none' },
      })
      // 带配置值的动态工具类
      matchUtilities(
        { 'grid-cols-auto': (value) => ({ gridTemplateColumns: `repeat(auto-fill, minmax(${value}, 1fr))` }) },
        { values: theme('spacing') }
      )
    }),
  ],
}
// 使用方式:<div class="grid-cols-auto-64">

7. Optimiser pour la production

7. 生产环境优化

bash
undefined
bash
// 检查生成的CSS文件大小
npx tailwindcss -i ./src/app.css -o ./dist/out.css --minify
du -sh dist/out.css  # 典型生产环境目标:< 20 KB

// 分析未被清除的类(必要时使用safelist)
ts
// 为动态生成的类添加白名单(例:从数据库获取的颜色)
safelist: [
  'bg-red-500', 'bg-green-500', 'bg-blue-500',
  { pattern: /^(bg|text|border)-(brand|destructive)-\d{2,3}$/ },
]
@apply
规则:
仅用于
.css
文件中重复使用的基础组件 — 绝不能在JSX组件中使用。
css
/* base.css — 正确用法 */
.btn-primary {
  @apply px-4 py-2 bg-brand-500 text-white rounded-md hover:bg-brand-600;
}

Vérifier la taille du CSS généré

8. 类顺序与可维护性

npx tailwindcss -i ./src/app.css -o ./dist/out.css --minify du -sh dist/out.css # objectif : < 20 KB en prod typique
推荐顺序(Prettier插件可自动强制执行):
bash
npm install -D prettier-plugin-tailwindcss
布局(display、position)→ Flexbox/Grid → 尺寸 → 间距 → 排版 → 颜色 → 效果 → 交互

Analyser les classes non purgées (safelist si nécessaire)

反模式与常见陷阱


```ts
// Safelist pour classes générées dynamiquement (ex: couleur depuis DB)
safelist: [
  'bg-red-500', 'bg-green-500', 'bg-blue-500',
  { pattern: /^(bg|text|border)-(brand|destructive)-\d{2,3}$/ },
]
Règle
@apply
:
uniquement pour les composants de base répétés dans des fichiers
.css
— jamais dans des composants JSX.
css
/* base.css — OK */
.btn-primary {
  @apply px-4 py-2 bg-brand-500 text-white rounded-md hover:bg-brand-600;
}
陷阱问题解决方案
通过字符串拼接生成动态类生产环境会被清除使用完整类名或添加白名单
在每个JSX组件中使用
@apply
抵消实用优先的优势在JSX中保持内联样式
不使用
extend
进行覆盖
删除Tailwind默认值始终使用
theme.extend
忽略
tailwind-merge
类冲突(
p-2 p-4
→ 结果不可预测)
始终通过
cn()
处理
不遵循移动端优先的断点CSS样式不一致无前缀=移动端样式,然后添加
sm:
及以上断点
在类中硬编码十六进制颜色无法主题化
tailwind.config
@theme
中定义

8. Ordre et maintenabilité

2026年最佳实践

Ordre recommandé (Prettier plugin enforce automatiquement) :
bash
npm install -D prettier-plugin-tailwindcss
Layout (display, position) → Flexbox/Grid → Sizing → Spacing → Typography → Colors → Effects → Interactions

  • 新项目默认使用v4:CSS优先配置、支持P3/oklch、零JS配置。
  • 复合变体(v3.4+):
    [@media(hover:hover)]:hover:bg-brand-600
    用于精准识别支持hover的设备。
  • 谨慎使用任意值
    w-[327px]
    可偶尔使用,但绝不能用于系统令牌。
  • 可访问性:交互元素始终添加
    focus-visible:ring-2 focus-visible:ring-brand-500
    ;确定调色板前用
    oklch
    测试对比度。
  • 容器查询(v3官方插件,v4原生支持):对于可复用组件,优先使用
    @container
    而非基于视口的响应式设计。
html
<!-- v4容器查询示例 -->
<div class="@container">
  <div class="grid grid-cols-1 @sm:grid-cols-2 @lg:grid-cols-3">...</div>
</div>

Anti-patterns et pièges

沟通规则 — 强制执行

PiègeProblèmeSolution
Classes dynamiques par concaténation de stringPurgées en prodUtiliser des classes complètes ou safelist
@apply
dans chaque composant JSX
Annule le bénéfice utility-firstGarder les styles inline en JSX
Override sans
extend
Supprime les valeurs Tailwind par défautToujours utiliser
theme.extend
Ignorer
tailwind-merge
Conflits de classes (
p-2 p-4
→ imprévisible)
Toujours passer par
cn()
Breakpoints hors mobile-firstCSS incohérentBase sans préfixe = mobile, puis
sm:
et au-delà
Hardcoder des couleurs hex en classesPas thémableDéfinir dans
tailwind.config
ou
@theme

  • 极度简洁。无冗余内容、无开场白、无客套话。
  • 绝不说“很高兴帮忙”、“当然!”、“好问题”、“让我”或类似表述。
  • 先行动,后沟通。先执行再解释。
  • 结果优先。先给出结果,而非过程。
  • 完成即停止。无总结、无回顾、无多余评论。
  • 无礼貌套话。直接、坦率。
  • 用词极简。能用一个词就不用十个词。
  • 不主动解释。
  • 除非要求,否则不使用表情符号。

Bonnes pratiques 2026

  • v4 par défaut pour tout projet neuf : config CSS-first, support P3/oklch, zero config JS.
  • Variants composés (v3.4+) :
    [@media(hover:hover)]:hover:bg-brand-600
    pour cibler les vrais hover devices.
  • Arbitrary values avec modération :
    w-[327px]
    acceptable ponctuellement, jamais pour les tokens systèmes.
  • Accessibilité : toujours inclure
    focus-visible:ring-2 focus-visible:ring-brand-500
    sur les éléments interactifs ; tester le contraste avec
    oklch
    avant de figer la palette.
  • Container queries (plugin officiel v3, natif v4) : préférer
    @container
    au responsive basé sur viewport pour les composants réutilisables.
html
<!-- Container query v4 -->
<div class="@container">
  <div class="grid grid-cols-1 @sm:grid-cols-2 @lg:grid-cols-3">...</div>
</div>

Communication Rules — MANDATORY

  • Ultra-concise. No filler, no preamble, no pleasantries.
  • Never say "happy to help", "sure!", "great question", "let me", or similar.
  • Tool first, talk second. Act before explaining.
  • Result first. Lead with outcome, not process.
  • Stop when done. No summary, no recap, no trailing commentary.
  • No politeness wrappers. Direct and blunt.
  • Minimum words. If one word works, do not use ten.
  • No unsolicited explanations.
  • No emoji unless asked.