styling-react-with-tailwind

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
References:
  • Guide - details on token system and patterns
参考资料:
  • 指南 - 详细介绍令牌系统与模式

Overview

概述

This codebase uses:
  • Tailwind CSS with semantic design tokens via
    tw-colors
    plugin
  • tailwind-variants (
    tv()
    ) for type-safe variant styling
  • Rose Pine color palette (light: Dawn, dark: Moon)
本代码库使用:
  • 结合
    tw-colors
    插件实现语义化设计令牌的Tailwind CSS
  • 用于类型安全变体样式的tailwind-variants
    tv()
  • Rose Pine调色板(浅色模式:Dawn,深色模式:Moon)

Token System

令牌系统

Color Token Pattern

颜色令牌规则

{property}-{category}-{semantic}
Text tokens:
  • text-text-base
    - Primary body text
  • text-text-muted
    - Secondary/subtle text
  • text-text-link
    - Interactive link color (iris purple)
  • text-text-strong
    - Emphasized text
Background tokens:
  • bg-background-base
    - Page background
  • bg-background-card
    - Card surfaces
  • bg-background-button
    - Button backgrounds
  • bg-background-informative
    - Info callouts
Border tokens:
  • border-border-muted
    - Subtle borders
  • border-border-input
    - Form field borders
  • border-border-informative
    - Info borders
{property}-{category}-{semantic}
文本令牌:
  • text-text-base
    - 主要正文文本
  • text-text-muted
    - 次要/弱化文本
  • text-text-link
    - 交互链接颜色(鸢尾紫)
  • text-text-strong
    - 强调文本
背景令牌:
  • bg-background-base
    - 页面背景
  • bg-background-card
    - 卡片表面
  • bg-background-button
    - 按钮背景
  • bg-background-informative
    - 信息提示框背景
边框令牌:
  • border-border-muted
    - 弱化边框
  • border-border-input
    - 表单字段边框
  • border-border-informative
    - 信息提示框边框

Spacing & Layout

间距与布局

Standard Tailwind spacing scale. Use semantic sizing:
  • p-4
    ,
    gap-2
    ,
    m-0
    - standard spacing
  • max-w-7xl
    - content width constraints
采用标准Tailwind间距刻度,使用语义化尺寸:
  • p-4
    ,
    gap-2
    ,
    m-0
    - 标准间距
  • max-w-7xl
    - 内容宽度限制

Component Patterns

组件模式

Pattern 1: Slots (Multi-element components)

模式1:Slots(多元素组件)

Use when component has multiple styled children.
tsx
import { tv, VariantProps } from 'tailwind-variants';
import { classnames } from '~/core/classnames';

const Styles = tv({
  slots: {
    root: ['flex', 'items-center', 'gap-2'],
    label: ['text-text-base', 'font-medium'],
    icon: ['text-text-muted', 'w-4', 'h-4'],
  },
  variants: {
    size: {
      sm: { root: 'p-2', label: 'text-sm' },
      md: { root: 'p-4', label: 'text-base' },
    },
    intent: {
      primary: { root: 'bg-background-button', label: 'text-text-strong' },
      ghost: { root: 'bg-transparent', label: 'text-text-muted' },
    },
  },
  defaultVariants: {
    size: 'md',
    intent: 'primary',
  },
});

type StyleProps = VariantProps<typeof Styles>;
type Props = StyleProps & {
  className?: string;
  children: React.ReactNode;
};

export function MyComponent(props: Props) {
  const styles = Styles({ size: props.size, intent: props.intent });
  return (
    <div className={classnames(styles.root(), props.className)}>
      <span className={styles.icon()}>*</span>
      <span className={styles.label()}>{props.children}</span>
    </div>
  );
}
适用于组件包含多个带样式子元素的场景。
tsx
import { tv, VariantProps } from 'tailwind-variants';
import { classnames } from '~/core/classnames';

const Styles = tv({
  slots: {
    root: ['flex', 'items-center', 'gap-2'],
    label: ['text-text-base', 'font-medium'],
    icon: ['text-text-muted', 'w-4', 'h-4'],
  },
  variants: {
    size: {
      sm: { root: 'p-2', label: 'text-sm' },
      md: { root: 'p-4', label: 'text-base' },
    },
    intent: {
      primary: { root: 'bg-background-button', label: 'text-text-strong' },
      ghost: { root: 'bg-transparent', label: 'text-text-muted' },
    },
  },
  defaultVariants: {
    size: 'md',
    intent: 'primary',
  },
});

type StyleProps = VariantProps<typeof Styles>;
type Props = StyleProps & {
  className?: string;
  children: React.ReactNode;
};

export function MyComponent(props: Props) {
  const styles = Styles({ size: props.size, intent: props.intent });
  return (
    <div className={classnames(styles.root(), props.className)}>
      <span className={styles.icon()}>*</span>
      <span className={styles.label()}>{props.children}</span>
    </div>
  );
}

Pattern 2: Base (Single-element components)

模式2:Base(单元素组件)

Use when component is a single styled element.
tsx
import { tv, VariantProps } from 'tailwind-variants';
import { classnames } from '~/core/classnames';

const Styles = tv({
  base: [
    'border',
    'border-border-muted',
    'rounded',
    'transition-colors',
  ],
  variants: {
    variant: {
      solid: 'border-2',
      dashed: 'border-dashed',
    },
  },
  defaultVariants: {
    variant: 'solid',
  },
});

type Props = VariantProps<typeof Styles> & {
  className?: string;
};

export function Divider(props: Props) {
  const styles = Styles({ variant: props.variant });
  return <hr className={classnames(styles, props.className)} />;
}
适用于组件为单个带样式元素的场景。
tsx
import { tv, VariantProps } from 'tailwind-variants';
import { classnames } from '~/core/classnames';

const Styles = tv({
  base: [
    'border',
    'border-border-muted',
    'rounded',
    'transition-colors',
  ],
  variants: {
    variant: {
      solid: 'border-2',
      dashed: 'border-dashed',
    },
  },
  defaultVariants: {
    variant: 'solid',
  },
});

type Props = VariantProps<typeof Styles> & {
  className?: string;
};

export function Divider(props: Props) {
  const styles = Styles({ variant: props.variant });
  return <hr className={classnames(styles, props.className)} />;
}

Pattern 3: Compound Variants

模式3:复合变体

For conditional style combinations:
tsx
const Styles = tv({
  base: ['rounded', 'px-4', 'py-2'],
  variants: {
    intent: { primary: '', danger: '' },
    disabled: { true: 'opacity-50 cursor-not-allowed' },
  },
  compoundVariants: [
    {
      intent: 'primary',
      disabled: false,
      class: 'bg-background-button hover:bg-background-hover',
    },
    {
      intent: 'danger',
      disabled: false,
      class: 'bg-red-500 hover:bg-red-600',
    },
  ],
});
用于条件样式组合:
tsx
const Styles = tv({
  base: ['rounded', 'px-4', 'py-2'],
  variants: {
    intent: { primary: '', danger: '' },
    disabled: { true: 'opacity-50 cursor-not-allowed' },
  },
  compoundVariants: [
    {
      intent: 'primary',
      disabled: false,
      class: 'bg-background-button hover:bg-background-hover',
    },
    {
      intent: 'danger',
      disabled: false,
      class: 'bg-red-500 hover:bg-red-600',
    },
  ],
});

Key Utilities

核心工具

classnames()

classnames()

Always use for merging classes safely:
tsx
import { classnames } from '~/core/classnames';

// Merges and deduplicates Tailwind classes
classnames(styles.root(), props.className, isActive && 'ring-2');
请始终使用该工具安全合并类名:
tsx
import { classnames } from '~/core/classnames';

// 合并并去重Tailwind类名
classnames(styles.root(), props.className, isActive && 'ring-2');

Box with asChild

带asChild的Box组件

For polymorphic components:
tsx
import { Box } from '~/components/ds/box/Box';

<Box asChild className="text-text-link">
  <a href="/path">Link styled as Box</a>
</Box>
用于多态组件:
tsx
import { Box } from '~/components/ds/box/Box';

<Box asChild className="text-text-link">
  <a href="/path">样式为Box的链接</a>
</Box>

Quick Reference

速查表

NeedUse
Multiple styled elements
tv({ slots: { ... } })
Single styled element
tv({ base: [...] })
Variant types
VariantProps<typeof Styles>
Merge classes
classnames(a, b, c)
Conditional variants
compoundVariants: [...]
Purple accent
text-text-link
,
text-text-strong
Muted text
text-text-muted
Card background
bg-background-card
需求解决方案
多个带样式元素
tv({ slots: { ... } })
单个带样式元素
tv({ base: [...] })
变体类型
VariantProps<typeof Styles>
合并类名
classnames(a, b, c)
条件变体
compoundVariants: [...]
紫色强调色
text-text-link
,
text-text-strong
弱化文本
text-text-muted
卡片背景
bg-background-card

Common Mistakes

常见错误

  1. Using raw colors - Always use semantic tokens (
    text-text-base
    not
    text-gray-900
    )
  2. Forgetting classnames() - Props className won't merge properly without it
  3. Destructuring props - Use
    props.x
    not
    { x }
    per codebase convention
  4. Missing VariantProps - Always type variants for IDE completion
  1. 直接使用原始颜色 - 请始终使用语义化令牌(如
    text-text-base
    而非
    text-gray-900
  2. 忘记使用classnames() - 不使用该工具的话,Props中的className无法正确合并
  3. 解构Props - 根据代码库约定,请使用
    props.x
    而非
    { x }
  4. 缺失VariantProps - 请始终为变体添加类型以获得IDE自动补全支持