WebStyling(React) web-styling

这个技能涵盖了React Web应用的样式模式,包括Tailwind CSS、CSS Modules、主题设计、响应式设计和组件样式。关键词:Tailwind CSS、CSS Modules、响应式设计、主题系统。

前端开发 0 次安装 0 次浏览 更新于 3/3/2026

Web Styling (React)

Tailwind CSS

基本用法

function Button({ variant = 'primary', children }) {
  const baseClasses = 'px-4 py-2 rounded-lg font-medium transition-colors';

  const variants = {
    primary: 'bg-blue-600 text-white hover:bg-blue-700',
    secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
    danger: 'bg-red-600 text-white hover:bg-red-700',
  };

  return (
    <button className={`${baseClasses} ${variants[variant]}`}>
      {children}
    </button>
  );
}

条件类与 clsx/cn

import { clsx } from 'clsx';
// 或者使用 tailwind-merge 进行去重
import { twMerge } from 'tailwind-merge';

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

function Card({ isActive, isDisabled, className, children }) {
  return (
    <div
      className={cn(
        'p-4 rounded-lg border',
        isActive && 'border-blue-500 bg-blue-50',
        isDisabled && 'opacity-50 cursor-not-allowed',
        className // 允许覆盖
      )}
    >
      {children}
    </div>
  );
}

响应式设计

// 移动优先断点
// sm: 640px, md: 768px, lg: 1024px, xl: 1280px, 2xl: 1536px

<div className="
  grid
  grid-cols-1      /* 移动: 1列 */
  sm:grid-cols-2   /* 平板: 2列 */
  lg:grid-cols-3   /* 桌面: 3列 */
  xl:grid-cols-4   /* 大屏: 4列 */
  gap-4
">
  {items.map(item => <Card key={item.id} {...item} />)}
</div>

// 响应式文本
<h1 className="text-2xl md:text-3xl lg:text-4xl font-bold">
  标题
</h1>

// 在断点处隐藏/显示
<nav className="hidden md:flex">桌面导航</nav>
<nav className="flex md:hidden">移动导航</nav>

暗色模式

// tailwind.config.js
module.exports = {
  darkMode: 'class', // 或 'media' 以使用操作系统偏好
  // ...
};

// 组件
<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
  <h1 className="text-black dark:text-white">标题</h1>
  <p className="text-gray-600 dark:text-gray-400">描述</p>
</div>

// 切换暗色模式
function ThemeToggle() {
  const [isDark, setIsDark] = useState(false);

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

  return (
    <button onClick={() => setIsDark(!isDark)}>
      {isDark ? '☀️' : '🌙'}
    </button>
  );
}

自定义设计令牌

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        brand: {
          50: '#f0f9ff',
          100: '#e0f2fe',
          500: '#0ea5e9',
          600: '#0284c7',
          700: '#0369a1',
        },
      },
      fontFamily: {
        sans: ['Inter', 'system-ui', 'sans-serif'],
      },
      spacing: {
        '18': '4.5rem',
        '88': '22rem',
      },
    },
  },
};
// 使用
<button className="bg-brand-500 hover:bg-brand-600">
  品牌按钮
</button>

CSS Modules

基本用法

/* Button.module.css */
.button {
  padding: 0.5rem 1rem;
  border-radius: 0.5rem;
  font-weight: 500;
}

.primary {
  background-color: #3b82f6;
  color: white;
}

.secondary {
  background-color: #e5e7eb;
  color: #1f2937;
}
// Button.tsx
import styles from './Button.module.css';

function Button({ variant = 'primary', children }) {
  return (
    <button className={`${styles.button} ${styles[variant]}`}>
      {children}
    </button>
  );
}

与 clsx 一起使用

import styles from './Card.module.css';
import { clsx } from 'clsx';

function Card({ isActive, className, children }) {
  return (
    <div
      className={clsx(
        styles.card,
        isActive && styles.active,
        className
      )}
    >
      {children}
    </div>
  );
}

CSS-in-JS (styled-components)

import styled from 'styled-components';

const Button = styled.button<{ variant?: 'primary' | 'secondary' }>`
  padding: 0.5rem 1rem;
  border-radius: 0.5rem;
  font-weight: 500;
  transition: background-color 0.2s;

  ${({ variant = 'primary' }) =>
    variant === 'primary'
      ? `
        background-color: #3b82f6;
        color: white;
        &:hover {
          background-color: #2563eb;
        }
      `
      : `
        background-color: #e5e7eb;
        color: #1f2937;
        &:hover {
          background-color: #d1d5db;
        }
      `}
`;

// 与主题一起使用
import { ThemeProvider } from 'styled-components';

const theme = {
  colors: {
    primary: '#3b82f6',
    secondary: '#e5e7eb',
  },
  spacing: {
    sm: '0.5rem',
    md: '1rem',
  },
};

const ThemedButton = styled.button`
  background-color: ${({ theme }) => theme.colors.primary};
  padding: ${({ theme }) => theme.spacing.md};
`;

组件变体模式

// 使用 cva (class-variance-authority) 与 Tailwind
import { cva, type VariantProps } from 'class-variance-authority';

const buttonVariants = cva(
  'inline-flex items-center justify-center rounded-md font-medium transition-colors',
  {
    variants: {
      variant: {
        primary: 'bg-blue-600 text-white hover:bg-blue-700',
        secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200',
        outline: 'border border-gray-300 hover:bg-gray-50',
        ghost: 'hover:bg-gray-100',
        danger: 'bg-red-600 text-white hover:bg-red-700',
      },
      size: {
        sm: 'h-8 px-3 text-sm',
        md: 'h-10 px-4',
        lg: 'h-12 px-6 text-lg',
      },
    },
    defaultVariants: {
      variant: 'primary',
      size: 'md',
    },
  }
);

interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {}

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

// 使用
<Button variant="primary" size="lg">大号主要</Button>
<Button variant="outline">轮廓</Button>

动画模式

Tailwind 动画

// 内置动画
<div className="animate-spin">加载中...</div>
<div className="animate-pulse">加载中...</div>
<div className="animate-bounce">向下滚动</div>

// 过渡
<button className="transition-all duration-200 hover:scale-105">
  悬停我
</button>

// 在 tailwind.config.js 中自定义动画
module.exports = {
  theme: {
    extend: {
      animation: {
        'fade-in': 'fadeIn 0.3s ease-out',
        'slide-up': 'slideUp 0.3s ease-out',
      },
      keyframes: {
        fadeIn: {
          '0%': { opacity: '0' },
          '100%': { opacity: '1' },
        },
        slideUp: {
          '0%': { transform: 'translateY(10px)', opacity: '0' },
          '100%': { transform: 'translateY(0)', opacity: '1' },
        },
      },
    },
  },
};

// 使用
<div className="animate-fade-in">渐入...</div>

Framer Motion

import { motion, AnimatePresence } from 'framer-motion';

function Modal({ isOpen, onClose, children }) {
  return (
    <AnimatePresence>
      {isOpen && (
        <>
          <motion.div
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="fixed inset-0 bg-black/50"
            onClick={onClose}
          />
          <motion.div
            initial={{ opacity: 0, scale: 0.95, y: 20 }}
            animate={{ opacity: 1, scale: 1, y: 0 }}
            exit={{ opacity: 0, scale: 0.95, y: 20 }}
            className="fixed inset-x-4 top-1/2 -translate-y-1/2 bg-white rounded-lg p-6"
          >
            {children}
          </motion.div>
        </>
      )}
    </AnimatePresence>
  );
}

布局模式

Flexbox

// 居中
<div className="flex items-center justify-center min-h-screen">
  <Card>居中内容</Card>
</div>

// 间隔
<div className="flex items-center justify-between">
  <Logo />
  <Navigation />
  <UserMenu />
</div>

// 响应式方向
<div className="flex flex-col md:flex-row gap-4">
  <Sidebar />
  <Main />
</div>

Grid

// 等宽列
<div className="grid grid-cols-3 gap-4">
  {items.map(item => <Card key={item.id} {...item} />)}
</div>

// 复杂布局
<div className="grid grid-cols-12 gap-4">
  <aside className="col-span-3">侧边栏</aside>
  <main className="col-span-6">主要内容</main>
  <aside className="col-span-3">右侧边栏</aside>
</div>

// 自适应未知数量
<div className="grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-4">
  {items.map(item => <Card key={item.id} {...item} />)}
</div>

容器

// 居中容器,最大宽度
<div className="container mx-auto px-4">
  <Content />
</div>

// 或自定义最大宽度
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
  <Content />
</div>

主题系统

// theme.ts
export const theme = {
  colors: {
    primary: {
      50: '#eff6ff',
      500: '#3b82f6',
      600: '#2563eb',
      700: '#1d4ed8',
    },
    gray: {
      50: '#f9fafb',
      100: '#f3f4f6',
      900: '#111827',
    },
  },
  spacing: {
    xs: '0.25rem',
    sm: '0.5rem',
    md: '1rem',
    lg: '1.5rem',
    xl: '2rem',
  },
} as const;

// CSS变量方法
:root {
  --color-primary: #3b82f6;
  --color-background: #ffffff;
  --color-text: #111827;
}

.dark {
  --color-primary: #60a5fa;
  --color-background: #111827;
  --color-text: #f9fafb;
}

// 使用Tailwind
<div className="bg-[var(--color-background)] text-[var(--color-text)]">
  内容
</div>

常见问题

问题 解决方案
样式不应用 检查类特异性,Tailwind 清除
暗色模式闪烁 使用CSS变量或SSR安全方法
布局偏移 设置明确的尺寸,使用骨架加载器
移动端溢出 向body添加overflow-x-hidden
Z-index冲突 使用一致的z-index规模

文件结构

styles/
  globals.css          # 全局样式,Tailwind导入
  variables.css        # CSS自定义属性
components/
  Button/
    Button.tsx
    Button.module.css  # 如果使用CSS Modules
    index.ts
tailwind.config.js     # Tailwind配置