component-variants

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Component Variants Pattern

组件变体模式

Create matching light and dark variants of UI components using a systematic color token approach.
使用系统化的颜色令牌方法创建匹配的UI组件亮色和暗色变体。

Pattern Overview

模式概述

  1. Create paired components:
    ComponentLight
    and
    ComponentDark
  2. Mirror the structure exactly between variants
  3. Map colors systematically using the token table below
  4. Export a unified component that renders both or accepts a
    variant
    prop
  1. 创建配对组件:
    ComponentLight
    ComponentDark
  2. 变体之间的结构完全镜像
  3. 使用下方的令牌表系统化映射颜色
  4. 导出一个可渲染两种变体或接受
    variant
    属性的统一组件

Color Token Mapping

颜色令牌映射

Basic Tokens

基础令牌

Semantic UseLight ModeDark Mode
Card background
bg-[#f8f8f8]
bg-zinc-800
Card border
border-zinc-200/80
border-zinc-700/80
Content area
bg-white
bg-zinc-900
Primary text
text-zinc-900
text-zinc-100
Secondary text
text-zinc-600
text-zinc-400
Muted text
text-zinc-500
text-zinc-500
Icon color
text-zinc-400
text-zinc-500
Tag background
bg-zinc-100
bg-zinc-800 border border-zinc-700
Tag text
text-zinc-600
text-zinc-400
Accent background
bg-green-50
bg-green-900/50
Accent text
text-green-700
text-green-400
Shadow
shadow-zinc-200/50
shadow-black/30
Hover background
hover:bg-zinc-100/50
hover:bg-zinc-700/30
语义用途亮色模式暗色模式
卡片背景
bg-[#f8f8f8]
bg-zinc-800
卡片边框
border-zinc-200/80
border-zinc-700/80
内容区域
bg-white
bg-zinc-900
主文本
text-zinc-900
text-zinc-100
次要文本
text-zinc-600
text-zinc-400
弱化文本
text-zinc-500
text-zinc-500
图标颜色
text-zinc-400
text-zinc-500
标签背景
bg-zinc-100
bg-zinc-800 border border-zinc-700
标签文本
text-zinc-600
text-zinc-400
强调背景
bg-green-50
bg-green-900/50
强调文本
text-green-700
text-green-400
阴影
shadow-zinc-200/50
shadow-black/30
悬停背景
hover:bg-zinc-100/50
hover:bg-zinc-700/30

Gradient & Nested Card Tokens

渐变与嵌套卡片令牌

Semantic UseLight ModeDark Mode
Outer card gradient
bg-gradient-to-b from-[#e8e8ea] to-[#dcdce0]
bg-gradient-to-b from-zinc-700 to-zinc-800
Outer card border
border-white/60
border-zinc-600/40
Outer card shadow
shadow-xl shadow-zinc-400/30
shadow-xl shadow-black/50
Inner media border
border-white/40
border-zinc-600/30
语义用途亮色模式暗色模式
外层卡片渐变
bg-gradient-to-b from-[#e8e8ea] to-[#dcdce0]
bg-gradient-to-b from-zinc-700 to-zinc-800
外层卡片边框
border-white/60
border-zinc-600/40
外层卡片阴影
shadow-xl shadow-zinc-400/30
shadow-xl shadow-black/50
内层媒体边框
border-white/40
border-zinc-600/30

Badge Tokens

徽章令牌

Semantic UseLight ModeDark Mode
Success badge bg
bg-[#d4f5d4]
bg-emerald-900/40
Success badge text
text-zinc-700
text-emerald-400
语义用途亮色模式暗色模式
成功徽章背景
bg-[#d4f5d4]
bg-emerald-900/40
成功徽章文本
text-zinc-700
text-emerald-400

Separator Tokens

分隔符令牌

Semantic UseLight ModeDark Mode
Dot separator
text-zinc-300
text-zinc-600
Border separator
border-zinc-300
border-zinc-700
语义用途亮色模式暗色模式
点分隔符
text-zinc-300
text-zinc-600
边框分隔符
border-zinc-300
border-zinc-700

Implementation Template

实现模板

tsx
// 1. Create the Light variant
function ComponentLight() {
  return (
    <div className="bg-[#f8f8f8] rounded-xl border border-zinc-200/80 shadow-lg shadow-zinc-200/50">
      <div className="bg-white px-4 py-4">
        <span className="text-zinc-900">Primary content</span>
        <span className="text-zinc-500">Secondary content</span>
      </div>
    </div>
  );
}

// 2. Create the Dark variant (mirror structure, swap tokens)
function ComponentDark() {
  return (
    <div className="bg-zinc-800 rounded-xl border border-zinc-700/80 shadow-lg shadow-black/30">
      <div className="bg-zinc-900 px-4 py-4">
        <span className="text-zinc-100">Primary content</span>
        <span className="text-zinc-500">Secondary content</span>
      </div>
    </div>
  );
}

// 3. Export unified component
export function Component({ variant = "light" }: { variant?: "light" | "dark" }) {
  return variant === "dark" ? <ComponentDark /> : <ComponentLight />;
}
tsx
// 1. Create the Light variant
function ComponentLight() {
  return (
    <div className="bg-[#f8f8f8] rounded-xl border border-zinc-200/80 shadow-lg shadow-zinc-200/50">
      <div className="bg-white px-4 py-4">
        <span className="text-zinc-900">Primary content</span>
        <span className="text-zinc-500">Secondary content</span>
      </div>
    </div>
  );
}

// 2. Create the Dark variant (mirror structure, swap tokens)
function ComponentDark() {
  return (
    <div className="bg-zinc-800 rounded-xl border border-zinc-700/80 shadow-lg shadow-black/30">
      <div className="bg-zinc-900 px-4 py-4">
        <span className="text-zinc-100">Primary content</span>
        <span className="text-zinc-500">Secondary content</span>
      </div>
    </div>
  );
}

// 3. Export unified component
export function Component({ variant = "light" }: { variant?: "light" | "dark" }) {
  return variant === "dark" ? <ComponentDark /> : <ComponentLight />;
}

Helper Components Pattern

辅助组件模式

When components have repeated sub-elements, create paired helpers:
tsx
interface InfoRowProps {
  icon: React.ReactNode;
  label: string;
  value: React.ReactNode;
}

function InfoRowLight({ icon, label, value }: InfoRowProps) {
  return (
    <div className="flex items-center gap-3 py-1">
      <span className="text-zinc-400">{icon}</span>
      <span className="text-zinc-500 text-sm w-32">{label}</span>
      <div className="flex-1">{value}</div>
    </div>
  );
}

function InfoRowDark({ icon, label, value }: InfoRowProps) {
  return (
    <div className="flex items-center gap-3 py-1">
      <span className="text-zinc-500">{icon}</span>
      <span className="text-zinc-500 text-sm w-32">{label}</span>
      <div className="flex-1">{value}</div>
    </div>
  );
}
当组件包含重复子元素时,创建配对的辅助组件:
tsx
interface InfoRowProps {
  icon: React.ReactNode;
  label: string;
  value: React.ReactNode;
}

function InfoRowLight({ icon, label, value }: InfoRowProps) {
  return (
    <div className="flex items-center gap-3 py-1">
      <span className="text-zinc-400">{icon}</span>
      <span className="text-zinc-500 text-sm w-32">{label}</span>
      <div className="flex-1">{value}</div>
    </div>
  );
}

function InfoRowDark({ icon, label, value }: InfoRowProps) {
  return (
    <div className="flex items-center gap-3 py-1">
      <span className="text-zinc-500">{icon}</span>
      <span className="text-zinc-500 text-sm w-32">{label}</span>
      <div className="flex-1">{value}</div>
    </div>
  );
}

Checklist

检查清单

  • Light and dark variants have identical structure
  • All color classes mapped according to token table
  • Text contrast meets accessibility guidelines
  • Shadows appropriate for each theme
  • Hover/focus states defined for both themes
  • 亮色和暗色变体结构完全一致
  • 所有颜色类均根据令牌表映射
  • 文本对比度符合无障碍指南
  • 阴影适配对应主题
  • 两种主题均定义了悬停/聚焦状态