正在加载,请稍候…

Tailwind CSS:构建可复用组件与设计系统

使用 Tailwind CSS 构建一致的 UI。学习组件模式、自定义设计令牌、暗色模式、响应式设计以及用于可复用样式的 @apply 指令。

Tailwind CSS:构建可复用组件与设计系统

Tailwind CSS:构建可复用组件

配置与设计令牌

// tailwind.config.js
module.exports = {
  content: ['./src/**/*.{ts,tsx,html}'],
  theme: {
    extend: {
      colors: {
        brand: {
          50:  '#eff6ff',
          100: '#dbeafe',
          500: '#3b82f6',
          600: '#2563eb',
          700: '#1d4ed8',
          900: '#1e3a8a',
        },
        danger: '#ef4444',
        success: '#22c55e',
      },
      fontFamily: {
        sans: ['Inter', 'system-ui', 'sans-serif'],
        mono: ['JetBrains Mono', 'monospace'],
      },
      spacing: {
        18: '4.5rem',
        88: '22rem',
      },
      animation: {
        'slide-in': 'slideIn 0.2s ease-out',
      },
      keyframes: {
        slideIn: {
          '0%': { transform: 'translateY(-10px)', opacity: '0' },
          '100%': { transform: 'translateY(0)', opacity: '1' },
        },
      },
    },
  },
  plugins: [
    require('@tailwindcss/forms'),
    require('@tailwindcss/typography'),
  ],
};

Tailwind CSS:构建可复用组件与设计系统插图

使用 cva 实现组件变体

import { cva, type VariantProps } from 'class-variance-authority';

const button = cva(
  // 基础样式
  'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50',
  {
    variants: {
      variant: {
        primary: 'bg-brand-600 text-white hover:bg-brand-700 focus-visible:ring-brand-500',
        secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200',
        outline: 'border border-gray-300 bg-transparent hover:bg-gray-50',
        ghost: 'hover:bg-gray-100',
        danger: 'bg-danger text-white hover:bg-red-600',
      },
      size: {
        sm: 'h-8 px-3 text-sm',
        md: 'h-10 px-4',
        lg: 'h-12 px-6 text-lg',
        icon: 'h-10 w-10',
      },
    },
    defaultVariants: {
      variant: 'primary',
      size: 'md',
    },
  }
);

interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof button> {
  loading?: boolean;
}

export function Button({ variant, size, className, loading, children, ...props }: ButtonProps) {
  return (
    <button
      className={button({ variant, size, className })}
      disabled={loading || props.disabled}
      {...props}
    >
      {loading && <Spinner className="mr-2 h-4 w-4" />}
      {children}
    </button>
  );
}

Tailwind CSS:构建可复用组件与设计系统插图

暗色模式

// tailwind.config.js
module.exports = {
  darkMode: 'class', // 或 'media' 以跟随系统偏好
};
// 主题切换
import { useState, useEffect } from 'react';

function useTheme() {
  const [theme, setTheme] = useState<'light' | 'dark'>(() => {
    if (typeof window === 'undefined') return 'light';
    return (localStorage.getItem('theme') as 'light' | 'dark') ?? 'light';
  });

  useEffect(() => {
    document.documentElement.classList.toggle('dark', theme === 'dark');
    localStorage.setItem('theme', theme);
  }, [theme]);

  return { theme, toggle: () => setTheme(t => t === 'light' ? 'dark' : 'light') };
}

// 在组件中使用
<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
  <h1 className="text-2xl font-bold text-gray-900 dark:text-white">Hello</h1>
</div>

Tailwind CSS:构建可复用组件与设计系统插图

响应式设计模式

<!-- 移动优先的响应式网格 -->
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
  <!-- 卡片 -->
</div>

<!-- 响应式侧边栏布局 -->
<div class="flex flex-col lg:flex-row gap-6">
  <aside class="w-full lg:w-64 shrink-0">侧边栏</aside>
  <main class="flex-1 min-w-0">主内容</main>
</div>

<!-- 在断点处显示/隐藏 -->
<nav class="hidden md:flex gap-4">桌面导航</nav>
<button class="md:hidden">汉堡菜单</button>

Typography 插件

<!-- 使用 prose 类处理富文本内容 -->
<article class="prose prose-gray dark:prose-invert max-w-none lg:prose-xl">
  <!-- Markdown 渲染的 HTML -->
</article>

Tailwind 的实用优先方法消除了命名摩擦,并实现了快速原型设计。