tailwind-conventions

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Tailwind CSS Conventions Skill

Tailwind CSS 约定规范 Skill

Consistent patterns for Tailwind CSS in React/Next.js applications.
适用于React/Next.js应用的Tailwind CSS统一模式。

Class Organization Order

类组织顺序

Always organize classes in this order for readability:
tsx
className={cn(
  // 1. Layout (position, display, flex/grid)
  "relative flex flex-col",
  // 2. Sizing (width, height, padding, margin)
  "w-full max-w-md p-4 mx-auto",
  // 3. Visual (background, border, shadow)
  "bg-white border border-gray-200 rounded-lg shadow-sm",
  // 4. Typography (font, text, color)
  "text-sm font-medium text-gray-900",
  // 5. States (hover, focus, active, disabled)
  "hover:bg-gray-50 focus:ring-2 focus:ring-blue-500",
  // 6. Responsive (sm:, md:, lg:, xl:)
  "md:flex-row md:p-6",
  // 7. Dark mode
  "dark:bg-gray-800 dark:text-white"
)}
为了可读性,请始终按照以下顺序组织类:
tsx
className={cn(
  // 1. 布局(position、display、flex/grid)
  "relative flex flex-col",
  // 2. 尺寸(width、height、padding、margin)
  "w-full max-w-md p-4 mx-auto",
  // 3. 视觉样式(background、border、shadow)
  "bg-white border border-gray-200 rounded-lg shadow-sm",
  // 4. 排版(font、text、color)
  "text-sm font-medium text-gray-900",
  // 5. 状态(hover、focus、active、disabled)
  "hover:bg-gray-50 focus:ring-2 focus:ring-blue-500",
  // 6. 响应式(sm:、md:、lg:、xl:)
  "md:flex-row md:p-6",
  // 7. 暗色模式
  "dark:bg-gray-800 dark:text-white"
)}

Utility Function (cn)

工具函数(cn)

Use
clsx
+
tailwind-merge
for conditional classes:
tsx
// lib/utils.ts
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';

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

// Usage
<div className={cn(
  "base-classes",
  isActive && "active-classes",
  variant === "primary" && "primary-classes"
)} />
使用
clsx
+
tailwind-merge
处理条件类:
tsx
// lib/utils.ts
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';

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

// 使用示例
<div className={cn(
  "base-classes",
  isActive && "active-classes",
  variant === "primary" && "primary-classes"
)} />

Responsive Design

响应式设计

Mobile-First Approach

移动端优先方案

tsx
// Start with mobile, add breakpoints for larger screens
<div className="
  flex flex-col gap-2      // Mobile: stack vertically
  md:flex-row md:gap-4     // Tablet+: horizontal row
  lg:gap-6                 // Desktop: more spacing
" />
tsx
// 从移动端样式开始,为更大屏幕添加断点
<div className="
  flex flex-col gap-2      // 移动端:垂直堆叠
  md:flex-row md:gap-4     // 平板及以上:水平排列
  lg:gap-6                 // 桌面端:更大间距
" />

Common Breakpoints

常见断点

  • sm:
    - 640px+ (large phones)
  • md:
    - 768px+ (tablets)
  • lg:
    - 1024px+ (laptops)
  • xl:
    - 1280px+ (desktops)
  • 2xl:
    - 1536px+ (large screens)
  • sm:
    - 640px+(大屏手机)
  • md:
    - 768px+(平板)
  • lg:
    - 1024px+(笔记本电脑)
  • xl:
    - 1280px+(桌面显示器)
  • 2xl:
    - 1536px+(大屏显示器)

Responsive Typography

响应式排版

tsx
<h1 className="text-2xl md:text-3xl lg:text-4xl font-bold" />
<p className="text-sm md:text-base" />
tsx
<h1 className="text-2xl md:text-3xl lg:text-4xl font-bold" />
<p className="text-sm md:text-base" />

Dark Mode

暗色模式

Using CSS Variables (Recommended)

使用CSS变量(推荐)

css
/* globals.css */
:root {
  --background: 255 255 255;
  --foreground: 10 10 10;
}

.dark {
  --background: 10 10 10;
  --foreground: 255 255 255;
}
tsx
// tailwind.config.ts
theme: {
  extend: {
    colors: {
      background: 'rgb(var(--background) / <alpha-value>)',
      foreground: 'rgb(var(--foreground) / <alpha-value>)',
    }
  }
}

// Usage - no dark: prefix needed
<div className="bg-background text-foreground" />
css
/* globals.css */
:root {
  --background: 255 255 255;
  --foreground: 10 10 10;
}

.dark {
  --background: 10 10 10;
  --foreground: 255 255 255;
}
tsx
// tailwind.config.ts
theme: {
  extend: {
    colors: {
      background: 'rgb(var(--background) / <alpha-value>)',
      foreground: 'rgb(var(--foreground) / <alpha-value>)',
    }
  }
}

// 使用方式 - 无需dark:前缀
<div className="bg-background text-foreground" />

Using dark: Prefix

使用dark:前缀

tsx
<div className="
  bg-white text-gray-900
  dark:bg-gray-900 dark:text-white
" />
tsx
<div className="
  bg-white text-gray-900
  dark:bg-gray-900 dark:text-white
" />

Component Patterns

组件模式

Button Variants

按钮变体

tsx
const buttonVariants = {
  base: "inline-flex items-center justify-center rounded-md font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none",
  variants: {
    primary: "bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500",
    secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200 focus:ring-gray-500",
    outline: "border border-gray-300 bg-transparent hover:bg-gray-50 focus:ring-gray-500",
    ghost: "bg-transparent hover:bg-gray-100 focus:ring-gray-500",
    destructive: "bg-red-600 text-white hover:bg-red-700 focus:ring-red-500",
  },
  sizes: {
    sm: "h-8 px-3 text-sm",
    md: "h-10 px-4 text-sm",
    lg: "h-12 px-6 text-base",
  }
};
tsx
const buttonVariants = {
  base: "inline-flex items-center justify-center rounded-md font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none",
  variants: {
    primary: "bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500",
    secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200 focus:ring-gray-500",
    outline: "border border-gray-300 bg-transparent hover:bg-gray-50 focus:ring-gray-500",
    ghost: "bg-transparent hover:bg-gray-100 focus:ring-gray-500",
    destructive: "bg-red-600 text-white hover:bg-red-700 focus:ring-red-500",
  },
  sizes: {
    sm: "h-8 px-3 text-sm",
    md: "h-10 px-4 text-sm",
    lg: "h-12 px-6 text-base",
  }
};

Card Pattern

卡片模式

tsx
<div className="rounded-lg border border-gray-200 bg-white shadow-sm dark:border-gray-800 dark:bg-gray-950">
  <div className="p-6">
    <h3 className="text-lg font-semibold">Title</h3>
    <p className="text-sm text-gray-500 dark:text-gray-400">Description</p>
  </div>
</div>
tsx
<div className="rounded-lg border border-gray-200 bg-white shadow-sm dark:border-gray-800 dark:bg-gray-950">
  <div className="p-6">
    <h3 className="text-lg font-semibold">标题</h3>
    <p className="text-sm text-gray-500 dark:text-gray-400">描述</p>
  </div>
</div>

Input Pattern

输入框模式

tsx
<input className="
  flex h-10 w-full rounded-md border border-gray-300 bg-white px-3 py-2
  text-sm placeholder:text-gray-400
  focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
  disabled:cursor-not-allowed disabled:opacity-50
  dark:border-gray-700 dark:bg-gray-900
" />
tsx
<input className="
  flex h-10 w-full rounded-md border border-gray-300 bg-white px-3 py-2
  text-sm placeholder:text-gray-400
  focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
  disabled:cursor-not-allowed disabled:opacity-50
  dark:border-gray-700 dark:bg-gray-900
" />

Spacing Conventions

间距约定

Consistent Spacing Scale

统一间距比例

  • gap-1
    /
    p-1
    = 4px (tight)
  • gap-2
    /
    p-2
    = 8px (compact)
  • gap-4
    /
    p-4
    = 16px (default)
  • gap-6
    /
    p-6
    = 24px (comfortable)
  • gap-8
    /
    p-8
    = 32px (spacious)
  • gap-1
    /
    p-1
    = 4px(紧凑)
  • gap-2
    /
    p-2
    = 8px(紧密)
  • gap-4
    /
    p-4
    = 16px(默认)
  • gap-6
    /
    p-6
    = 24px(舒适)
  • gap-8
    /
    p-8
    = 32px(宽松)

Container Pattern

容器模式

tsx
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
  {/* Content */}
</div>
tsx
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
  {/* 内容 */}
</div>

Animation

动画

Transitions

过渡效果

tsx
// Smooth color/opacity transitions
<button className="transition-colors duration-200" />

// Transform transitions (scale, translate)
<div className="transition-transform duration-200 hover:scale-105" />

// All properties
<div className="transition-all duration-300" />
tsx
// 平滑的颜色/透明度过渡
<button className="transition-colors duration-200" />

// 变换过渡(缩放、平移)
<div className="transition-transform duration-200 hover:scale-105" />

// 所有属性过渡
<div className="transition-all duration-300" />

Built-in Animations

内置动画

tsx
<div className="animate-spin" />     // Loading spinner
<div className="animate-pulse" />    // Skeleton loading
<div className="animate-bounce" />   // Attention grabber
<div className="animate-ping" />     // Notification dot
tsx
<div className="animate-spin" />     // 加载旋转动画
<div className="animate-pulse" />    // 骨架屏加载动画
<div className="animate-bounce" />   // 注意力吸引动画
<div className="animate-ping" />     // 通知点动画

Accessibility

无障碍访问

Focus States

焦点状态

tsx
// Always visible focus rings
<button className="
  focus:outline-none
  focus:ring-2
  focus:ring-blue-500
  focus:ring-offset-2
" />

// Focus-visible for keyboard only
<button className="
  focus-visible:outline-none
  focus-visible:ring-2
  focus-visible:ring-blue-500
" />
tsx
// 始终可见的焦点环
<button className="
  focus:outline-none
  focus:ring-2
  focus:ring-blue-500
  focus:ring-offset-2
" />

// 仅键盘操作时显示焦点
<button className="
  focus-visible:outline-none
  focus-visible:ring-2
  focus-visible:ring-blue-500
" />

Screen Reader

屏幕阅读器

tsx
<span className="sr-only">Loading...</span>  // Hidden visually, announced
<div aria-hidden="true" className="..." />   // Decorative, ignored
tsx
<span className="sr-only">加载中...</span>  // 视觉隐藏,屏幕阅读器可读取
<div aria-hidden="true" className="..." />   // 装饰性元素,屏幕阅读器忽略

Layout Patterns

布局模式

CSS Grid Layouts

CSS Grid布局

tsx
// 12-column grid
<div className="grid grid-cols-12 gap-4">
  <div className="col-span-8">Main content</div>
  <div className="col-span-4">Sidebar</div>
</div>

// Auto-fit responsive grid
<div className="grid grid-cols-[repeat(auto-fit,minmax(300px,1fr))] gap-6">
  {items.map(item => <Card key={item.id} />)}
</div>

// Dashboard grid
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
  <StatCard />
  <StatCard />
  <StatCard />
  <StatCard />
</div>
tsx
// 12列网格
<div className="grid grid-cols-12 gap-4">
  <div className="col-span-8">主内容</div>
  <div className="col-span-4">侧边栏</div>
</div>

// 自适应响应式网格
<div className="grid grid-cols-[repeat(auto-fit,minmax(300px,1fr))] gap-6">
  {items.map(item => <Card key={item.id} />)}
</div>

// 仪表盘网格
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
  <StatCard />
  <StatCard />
  <StatCard />
  <StatCard />
</div>

Sidebar Layout

侧边栏布局

tsx
// Fixed sidebar + scrollable content
<div className="flex h-screen">
  <aside className="w-64 flex-shrink-0 border-r bg-gray-50 overflow-y-auto">
    <nav className="p-4">{/* Nav items */}</nav>
  </aside>
  <main className="flex-1 overflow-y-auto p-6">
    {/* Main content */}
  </main>
</div>

// Collapsible sidebar
<div className="flex h-screen">
  <aside className={cn(
    "flex-shrink-0 border-r bg-gray-50 transition-all duration-300",
    isOpen ? "w-64" : "w-16"
  )}>
    {/* Sidebar content */}
  </aside>
  <main className="flex-1">{/* Content */}</main>
</div>
tsx
// 固定侧边栏 + 可滚动内容
<div className="flex h-screen">
  <aside className="w-64 flex-shrink-0 border-r bg-gray-50 overflow-y-auto">
    <nav className="p-4">{/* 导航项 */}</nav>
  </aside>
  <main className="flex-1 overflow-y-auto p-6">
    {/* 主内容 */}
  </main>
</div>

// 可折叠侧边栏
<div className="flex h-screen">
  <aside className={cn(
    "flex-shrink-0 border-r bg-gray-50 transition-all duration-300",
    isOpen ? "w-64" : "w-16"
  )}>
    {/* 侧边栏内容 */}
  </aside>
  <main className="flex-1">{/* 内容 */}</main>
</div>

Form Layouts

表单布局

tsx
// Stacked form
<form className="space-y-4 max-w-md">
  <div>
    <label className="block text-sm font-medium mb-1">Email</label>
    <input className="w-full rounded-md border px-3 py-2" />
  </div>
  <div>
    <label className="block text-sm font-medium mb-1">Password</label>
    <input className="w-full rounded-md border px-3 py-2" type="password" />
  </div>
  <button className="w-full bg-blue-600 text-white rounded-md py-2">
    Sign In
  </button>
</form>

// Inline form (search bar)
<form className="flex gap-2">
  <input className="flex-1 rounded-md border px-3 py-2" placeholder="Search..." />
  <button className="px-4 py-2 bg-blue-600 text-white rounded-md">Search</button>
</form>

// Two-column form
<form className="grid grid-cols-1 md:grid-cols-2 gap-4">
  <div>
    <label className="block text-sm font-medium mb-1">First Name</label>
    <input className="w-full rounded-md border px-3 py-2" />
  </div>
  <div>
    <label className="block text-sm font-medium mb-1">Last Name</label>
    <input className="w-full rounded-md border px-3 py-2" />
  </div>
  <div className="md:col-span-2">
    <label className="block text-sm font-medium mb-1">Email</label>
    <input className="w-full rounded-md border px-3 py-2" />
  </div>
</form>
tsx
// 堆叠式表单
<form className="space-y-4 max-w-md">
  <div>
    <label className="block text-sm font-medium mb-1">邮箱</label>
    <input className="w-full rounded-md border px-3 py-2" />
  </div>
  <div>
    <label className="block text-sm font-medium mb-1">密码</label>
    <input className="w-full rounded-md border px-3 py-2" type="password" />
  </div>
  <button className="w-full bg-blue-600 text-white rounded-md py-2">
    登录
  </button>
</form>

// 内联表单(搜索栏)
<form className="flex gap-2">
  <input className="flex-1 rounded-md border px-3 py-2" placeholder="搜索..." />
  <button className="px-4 py-2 bg-blue-600 text-white rounded-md">搜索</button>
</form>

// 两列表单
<form className="grid grid-cols-1 md:grid-cols-2 gap-4">
  <div>
    <label className="block text-sm font-medium mb-1">名字</label>
    <input className="w-full rounded-md border px-3 py-2" />
  </div>
  <div>
    <label className="block text-sm font-medium mb-1">姓氏</label>
    <input className="w-full rounded-md border px-3 py-2" />
  </div>
  <div className="md:col-span-2">
    <label className="block text-sm font-medium mb-1">邮箱</label>
    <input className="w-full rounded-md border px-3 py-2" />
  </div>
</form>

Header/Footer Layout

页眉/页脚布局

tsx
// Sticky header + footer
<div className="min-h-screen flex flex-col">
  <header className="sticky top-0 z-50 border-b bg-white">
    <nav className="mx-auto max-w-7xl px-4 h-16 flex items-center justify-between">
      {/* Nav content */}
    </nav>
  </header>
  <main className="flex-1">
    {/* Page content */}
  </main>
  <footer className="border-t py-8">
    {/* Footer content */}
  </footer>
</div>
tsx
// 粘性页眉 + 页脚
<div className="min-h-screen flex flex-col">
  <header className="sticky top-0 z-50 border-b bg-white">
    <nav className="mx-auto max-w-7xl px-4 h-16 flex items-center justify-between">
      {/* 导航内容 */}
    </nav>
  </header>
  <main className="flex-1">
    {/* 页面内容 */}
  </main>
  <footer className="border-t py-8">
    {/* 页脚内容 */}
  </footer>
</div>

Centered Content

居中内容

tsx
// Horizontally centered with max-width
<div className="mx-auto max-w-4xl px-4">{/* Content */}</div>

// Vertically and horizontally centered
<div className="min-h-screen flex items-center justify-center">
  <div className="w-full max-w-md">{/* Centered card */}</div>
</div>
tsx
// 水平居中并限制最大宽度
<div className="mx-auto max-w-4xl px-4">{/* 内容 */}</div>

// 垂直和水平居中
<div className="min-h-screen flex items-center justify-center">
  <div className="w-full max-w-md">{/* 居中卡片 */}</div>
</div>

Anti-Patterns

反模式

  • Don't use
    @apply
    excessively - defeats Tailwind's purpose
  • Don't create overly specific custom classes
  • Don't fight the spacing scale - use what's provided
  • Don't inline complex conditional logic - extract to variables
  • Don't forget dark mode when adding colors
  • Don't use arbitrary values
    [123px]
    when scale values exist

  • 不要过度使用
    @apply
    ——这违背了Tailwind的设计初衷
  • 不要创建过于具体的自定义类
  • 不要抗拒使用内置的间距比例
  • 不要内联复杂的条件逻辑——提取到变量中
  • 添加颜色时不要忘记适配暗色模式
  • 当有内置比例值可用时,不要使用任意值
    [123px]

Version

版本

  • v1.0.0 (2025-12-05): Added YAML frontmatter, initial documented version
  • v1.0.0(2025-12-05):添加YAML前置元数据,初始文档版本