16-mini-program-ui.md 9.7 KB

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

概述

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

技术栈

  • Taro 4 - 跨端小程序框架
  • React 18 - 前端框架
  • Tailwind CSS v4 - 原子化CSS框架
  • @egoist/tailwindcss-icons - 图标库集成
  • 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 基础类名使用

// ✅ 正确使用原子类
<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 响应式设计

// 使用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 状态样式

// 悬停和焦点状态
<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 Heroicons图标

使用@egoist/tailwindcss-icons提供的图标类名:

// 基础用法
<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组件应包含:

// 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)

// 使用示例
<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)

<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)

<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 页面容器

// 主容器
<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 颜色系统

/* 在 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配置

// 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 类名合并工具

// mini/src/utils/cn.ts
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'

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

7. 最佳实践

7.1 状态管理

// 使用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 错误处理

// 错误状态展示
<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 开发调试

// 添加调试样式类
<View className="border border-red-500 debug">
  <Text>调试内容</Text>
</View>

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

注意事项

  1. 兼容性:确保所有类名在小程序环境中有效
  2. 性能:避免过度嵌套和复杂选择器
  3. 可维护性:保持组件结构清晰,样式统一
  4. 可读性:合理使用空格和换行,提高代码可读性