# 小程序UI开发规范 (Tailwind CSS v4)

## 概述

本规范定义了基于Taro框架的小程序UI开发标准，采用Tailwind CSS v4原子化样式和Heroicons图标库，遵循shadcn/ui组件设计模式。

## 技术栈

- **Taro 4** - 跨端小程序框架
- **React 18** - 前端框架
- **Tailwind CSS v4** - 原子化CSS框架
- **@egoist/tailwindcss-icons** - 图标库集成
- **@weapp-tailwindcss/merge** - Tailwind类名合并工具（小程序版tailwind-merge）
- **clsx** - 条件样式类名管理

## 目录结构

```
mini/
├── src/
│   ├── components/
│   │   └── ui/           # UI组件库
│   │       ├── button.tsx
│   │       ├── input.tsx
│   │       ├── card.tsx
│   │       └── ...
│   ├── pages/
│   ├── utils/
│   └── app.css           # Tailwind样式入口
├── tailwind.config.js    # Tailwind配置
└── postcss.config.js     # PostCSS配置
```

## 样式规范

### 1. Tailwind CSS v4 使用规范

#### 1.1 基础类名使用
```typescript
// ✅ 正确使用原子类
<View className="flex items-center justify-between p-4 bg-white rounded-lg shadow-sm">
  <Text className="text-lg font-semibold text-gray-900">标题</Text>
</View>

// ❌ 避免使用内联样式
<View style={{ display: 'flex', alignItems: 'center', padding: 16 }}>
  <Text style={{ fontSize: 18, fontWeight: '600' }}>标题</Text>
</View>
```

#### 1.2 类名合并规范
```typescript
// ✅ 使用twMerge处理动态类名冲突
import { twMerge } from '@weapp-tailwindcss/merge'

// 处理静态和动态类名的冲突
<View className={twMerge('px-4 py-2', isActive ? 'bg-blue-500' : 'bg-gray-200')}>
  <Text>按钮</Text>
</View>

// 处理多个条件类名的合并
<View className={twMerge(
  'flex items-center',
  isActive && 'bg-blue-500 text-white',
  isDisabled && 'opacity-50 cursor-not-allowed',
  customClassName
)}>
  <Text>复杂组件</Text>
</View>

// ❌ 避免手动拼接类名导致冲突
<View className={`px-4 py-2 ${isActive ? 'bg-blue-500' : 'bg-gray-200'} ${customClassName}`}>
  <Text>按钮</Text>
</View>
```

#### 1.2 响应式设计
```typescript
// 使用Tailwind的响应式前缀
<View className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
  <View className="w-full sm:w-1/2 md:w-1/3" />
</View>
```

#### 1.3 状态样式
```typescript
// 悬停和焦点状态
<Button className="bg-blue-500 hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">
  点击按钮
</Button>

// 禁用状态
<Button className="disabled:opacity-50 disabled:cursor-not-allowed">
  禁用按钮
</Button>
```

### 2. 图标使用规范

#### 2.1 图标
使用`@egoist/tailwindcss-icons`提供的图标类名：
"mdi", "lucide", "heroicons", "heroicons-outline", "heroicons-solid"

```typescript
// 基础用法
<View className="i-heroicons-user-20-solid text-gray-600" />
<Button className="flex items-center gap-2">
  <View className="i-heroicons-plus-20-solid" />
  <Text>添加</Text>
</Button>

// 图标大小和颜色
<View className="i-heroicons-home-16-solid w-6 h-6 text-blue-500" />
<View className="i-heroicons-cog-8-tooth-20-solid w-8 h-8 text-gray-400" />

// 图标变体
// solid - 实心图标
// outline - 轮廓图标
// mini - 迷你图标 (20x20)
// micro - 微型图标 (16x16)
<View className="i-heroicons-heart-20-solid text-red-500" />
<View className="i-heroicons-heart-20-outline text-red-500" />
```

#### 2.2 图标命名规则
```
i-heroicons-[图标名]-[大小]-[变体]
```
- 大小: 16 | 20 | 24
- 变体: solid | outline

### 3. UI组件规范

#### 3.1 组件文件结构
每个UI组件应包含：
```typescript
// mini/src/components/ui/button.tsx
import { Button as TaroButton, ButtonProps } from '@tarojs/components'
import { cn } from '@/utils/cn'
import { cva, type VariantProps } from 'class-variance-authority'

const buttonVariants = cva(
  'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background',
  {
    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 hover:bg-accent hover:text-accent-foreground',
        secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
        ghost: 'hover:bg-accent hover:text-accent-foreground',
        link: 'underline-offset-4 hover:underline text-primary',
      },
      size: {
        default: 'h-10 py-2 px-4',
        sm: 'h-9 px-3 rounded-md',
        lg: 'h-11 px-8 rounded-md',
        icon: 'h-10 w-10',
      },
    },
    defaultVariants: {
      variant: 'default',
      size: 'default',
    },
  }
)

interface ButtonProps extends ButtonProps, VariantProps<typeof buttonVariants> {}

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

#### 3.2 常用组件示例

**按钮组件 (Button)**
```typescript
// 使用示例
<Button variant="primary" size="lg" onClick={handleClick}>
  <View className="i-heroicons-plus-20-solid mr-2" />
  创建用户
</Button>

<Button variant="outline" size="sm" disabled={loading}>
  {loading && <View className="i-heroicons-arrow-path-20-solid animate-spin mr-2" />}
  加载中...
</Button>
```

**卡片组件 (Card)**
```typescript
<Card className="p-6 bg-white rounded-lg shadow-sm">
  <CardHeader>
    <View className="flex items-center justify-between">
      <Text className="text-lg font-semibold">用户信息</Text>
      <View className="i-heroicons-user-circle-20-solid text-gray-400" />
    </View>
  </CardHeader>
  <CardContent>
    <Text className="text-gray-600">用户详情内容</Text>
  </CardContent>
</Card>
```

**输入框组件 (Input)**
```typescript
<View className="space-y-2">
  <Label htmlFor="username">用户名</Label>
  <View className="relative">
    <View className="absolute left-3 top-1/2 -translate-y-1/2">
      <View className="i-heroicons-user-20-solid text-gray-400 w-5 h-5" />
    </View>
    <Input
      id="username"
      className="pl-10"
      placeholder="请输入用户名"
      value={username}
      onInput={handleInput}
    />
  </View>
</View>
```

### 4. 页面布局规范

#### 4.1 页面容器
```typescript
// 主容器
<View className="min-h-screen bg-gray-50">
  <View className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
    {/* 页面内容 */}
  </View>
</View>

// 卡片布局
<View className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
  {/* 卡片内容 */}
</View>
```

#### 4.2 响应式断点
- `sm`: 640px
- `md`: 768px  
- `lg`: 1024px
- `xl`: 1280px
- `2xl`: 1536px

### 5. 主题配置

#### 5.1 颜色系统
```css
/* 在 app.css 中定义 */
:root {
  --primary: 59 130 246;
  --primary-foreground: 255 255 255;
  --secondary: 107 114 128;
  --secondary-foreground: 255 255 255;
  --accent: 243 244 246;
  --accent-foreground: 17 24 39;
  --destructive: 239 68 68;
  --destructive-foreground: 255 255 255;
  --muted: 249 250 251;
  --muted-foreground: 107 114 128;
  --border: 229 231 235;
  --input: 255 255 255;
  --ring: 59 130 246;
  --background: 255 255 255;
  --foreground: 17 24 39;
}
```

#### 5.2 Tailwind配置
```javascript
// tailwind.config.js
module.exports = {
  content: [
    './src/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {
      colors: {
        primary: 'rgb(var(--primary))',
        'primary-foreground': 'rgb(var(--primary-foreground))',
        secondary: 'rgb(var(--secondary))',
        'secondary-foreground': 'rgb(var(--secondary-foreground))',
        accent: 'rgb(var(--accent))',
        'accent-foreground': 'rgb(var(--accent-foreground))',
        destructive: 'rgb(var(--destructive))',
        'destructive-foreground': 'rgb(var(--destructive-foreground))',
        muted: 'rgb(var(--muted))',
        'muted-foreground': 'rgb(var(--muted-foreground))',
        border: 'rgb(var(--border))',
        input: 'rgb(var(--input))',
        ring: 'rgb(var(--ring))',
        background: 'rgb(var(--background))',
        foreground: 'rgb(var(--foreground))',
      },
    },
  },
  plugins: [
    require('@egoist/tailwindcss-icons')({
      // 配置Heroicons
      collections: {
        heroicons: {
          solid: true,
          outline: true,
          mini: true,
        },
      },
    }),
  ],
}
```

### 6. 工具函数

#### 6.1 类名合并工具
```typescript
// mini/src/utils/cn.ts
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from '@weapp-tailwindcss/merge'

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

#### 6.2 小程序专用类名处理
```typescript
// 小程序环境下的类名合并
import { twMerge } from '@weapp-tailwindcss/merge'

// 标准用法（自动处理小程序转义）
const classes = twMerge('px-2 py-1 bg-red hover:bg-dark-red', 'p-3 bg-[#B91C1C]')
// → 'hovercbg-dark-red p-3 bg-_hB91C1C_'

// 手动指定版本（如果需要）
import { twMerge as twMergeV4 } from '@weapp-tailwindcss/merge/v4'
import { twMerge as twMergeV3 } from '@weapp-tailwindcss/merge/v3'

// 使用cva进行组件变体管理
import { cva } from 'class-variance-authority'

const buttonVariants = cva(
  'inline-flex items-center justify-center rounded-md text-sm font-medium',
  {
    variants: {
      variant: {
        default: 'bg-blue-500 text-white hover:bg-blue-600',
        destructive: 'bg-red-500 text-white hover:bg-red-600',
      },
      size: {
        sm: 'h-8 px-3 text-xs',
        lg: 'h-12 px-6 text-base',
      },
    },
  }
)
```

### 7. 最佳实践

#### 7.1 状态管理
```typescript
// 使用React Hook进行状态管理
const [loading, setLoading] = useState(false)
const [data, setData] = useState<User[]>([])

// 加载状态显示
{loading ? (
  <View className="flex justify-center py-8">
    <View className="i-heroicons-arrow-path-20-solid animate-spin w-8 h-8 text-blue-500" />
  </View>
) : (
  <View className="grid grid-cols-1 gap-4">
    {data.map(item => <Card key={item.id} {...item} />)}
  </View>
)}
```

#### 7.2 错误处理
```typescript
// 错误状态展示
<View className="text-center py-8">
  <View className="i-heroicons-exclamation-triangle-20-solid w-12 h-12 text-red-500 mx-auto mb-4" />
  <Text className="text-gray-600">加载失败，请稍后重试</Text>
  <Button variant="outline" size="sm" onClick={retry} className="mt-4">
    重新加载
  </Button>
</View>
```

### 8. 性能优化

#### 8.1 样式优化
- 使用Tailwind的JIT模式，只生成用到的类名
- 避免内联样式，全部使用类名
- 合理使用`@apply`提取重复样式

#### 8.2 图标优化
- 使用CSS图标而非图片图标
- 图标按需加载，不使用的图标不会被打包
- 合理使用图标大小，避免过大图标

### 9. 调试工具

#### 9.1 开发调试
```typescript
// 添加调试样式类
<View className="border border-red-500 debug">
  <Text>调试内容</Text>
</View>

// 使用Tailwind的调试工具
// 在开发环境中添加
// <View className="outline outline-1 outline-red-500" />
```

### 10. tailwind-merge使用规范

#### 10.1 基本用法
```typescript
// 单类名合并
const result = twMerge('px-2 py-1 bg-red hover:bg-dark-red', 'p-3 bg-[#B91C1C]')
// → 'hovercbg-dark-red p-3 bg-_hB91C1C_'

// 处理冲突类名
twMerge('px-4', 'px-2') // → 'px-2'
twMerge('text-red-500', 'text-blue-500') // → 'text-blue-500'
```

#### 10.2 条件类名处理
```typescript
// 使用cn工具函数处理条件类名
import { cn } from '@/utils/cn'

const Button = ({ variant, size, disabled, className }) => {
  return (
    <Button
      className={cn(
        'inline-flex items-center justify-center rounded-md',
        variant === 'primary' && 'bg-blue-500 text-white',
        variant === 'secondary' && 'bg-gray-200 text-gray-800',
        size === 'sm' && 'px-3 py-1 text-sm',
        size === 'lg' && 'px-6 py-3 text-lg',
        disabled && 'opacity-50 cursor-not-allowed',
        className // 允许外部覆盖
      )}
    >
      按钮
    </Button>
  )
}
```

#### 10.3 小程序特殊处理
```typescript
// 跨端使用
import { create } from '@weapp-tailwindcss/merge'

const { twMerge } = create({
  // 在当前环境为小程序时启用转义
  disableEscape: true
})

// 版本选择
import { twMerge as twMergeV4 } from '@weapp-tailwindcss/merge/v4' // Tailwind v4
import { twMerge as twMergeV3 } from '@weapp-tailwindcss/merge/v3' // Tailwind v3
```

## 注意事项

1. **兼容性**：确保所有类名在小程序环境中有效
2. **性能**：避免过度嵌套和复杂选择器
3. **可维护性**：保持组件结构清晰，样式统一
4. **可读性**：合理使用空格和换行，提高代码可读性
5. **tailwind-merge**：始终使用twMerge或cn工具函数处理动态类名，避免类名冲突
6. **版本兼容**：根据Tailwind CSS版本选择正确的tailwind-merge版本