AgentSkillsCN

design-system

为 Web 和 React Native 设计令牌系统和组件架构。 适用场景:实施设计系统、构建 UI 组件、定义令牌、创建主题化界面。 不适用于:UX 决策(使用 ux-design)、业务逻辑、数据获取。 工作流程:ux-design(什么/为什么)→ 本技能(令牌、组件)→ nextjs/expo-react-native(集成)。

SKILL.md
--- frontmatter
name: design-system
description: |
  Design token systems and component architecture for web and React Native.
  Use when: implementing design-system, building UI components, defining tokens, creating themed interfaces.
  Do not use for: UX decisions (use ux-design), business logic, data fetching.
  Workflow: ux-design (what/why) → this skill (tokens, components) → nextjs/expo-react-native (integration).
references:
  - token-examples.md    # W3C DTCG format examples, theme files
  - examples.md          # Headless hooks, Compound components examples

Design System

Token Architecture

Multi-Tier Hierarchy

code
Tier 1: Primitive     →  Raw values, no meaning
Tier 2: Semantic      →  Intent/purpose ← USE THIS
Tier 3: Component     →  (Optional) Component-specific overrides
code
color.blue.500              (primitive) - NEVER use directly
    ↓
color.interactive.primary   (semantic) ← use this in components
    ↓
button.bg.primary           (component, optional)

Rule: Components only reference Semantic tokens. Never Primitive.

Why 3 Tiers?

ChangeWhat to updateImpact
Brand refreshTier 1 (Primitive)All semantics auto-update
Dark modeTier 2 (Semantic)Components unchanged
One-off exceptionTier 3 (Component)Isolated change

Semantic Token Categories

CategoryPurposeExamples
color.bg.*Backgroundsprimary, secondary, inverse
color.text.*Typographyprimary, secondary, link
color.interactive.*Actionsprimary, primaryHover, primaryActive
color.border.*Bordersdefault, strong, focus
color.status.*Feedbackerror, success, warning
spacing.component.*Inside componentsxs, sm, md, lg, xl
spacing.layout.*Between sectionsxs, sm, md, lg, xl

Component Architecture

Headless + Styled Separation

code
┌──────────────────────────────────────┐
│  Headless Layer                      │
│  Behavior + A11y + Keyboard          │
│  No styles, reusable across brands   │
└──────────────────────────────────────┘
                 ↓
┌──────────────────────────────────────┐
│  Styled Layer                        │
│  Headless + Tokens = UI Component    │
└──────────────────────────────────────┘

Rule: Headless handles behavior. Styled handles appearance. Never mix.

Why Separate?

ChangeWhat to updateOther layer
Keyboard navigation fixHeadless hookStyled unchanged
Brand color changeStyled componentHeadless unchanged
New accessibility requirementHeadless hookStyled unchanged

File Structure

code
src/shared/ui/
├── tokens/          # primitive, semantic, themes
├── headless/        # useButton, useToggle, useDialog
├── styled/          # Button, Toggle, Dialog (uses headless)
├── primitives/      # Box, Text, Stack
└── patterns/        # FormField, ConfirmDialog

Preferred Patterns

PatternPreferredAvoidWhy
Propsvariant="primary" size="lg"primary large (booleans)Explicit, mutually exclusive
Composition<Card><Card.Header><Card showHeader headerTitle="">Flexible, readable
Polymorphic<Box as="section">Separate <Section> componentSemantic HTML, less components
StateHeadless hook + StyledMixed logic/stylesSeparation of concerns

Rule: Variant props over booleans. Composition over configuration.


Platform Output

Web (CSS Custom Properties)

css
:root {
  --color-bg-primary: #ffffff;
  --color-text-primary: #0f172a;
  --color-interactive-primary: #2563eb;
}

[data-theme="dark"] {
  --color-bg-primary: #0f172a;
  --color-text-primary: #f9fafb;
}

React Native (TypeScript)

typescript
export const tokens = {
  color: {
    bg: { primary: '#ffffff', secondary: '#f9fafb' },
    text: { primary: '#0f172a', secondary: '#475569' },
    interactive: { primary: '#2563eb' },
  },
  spacing: {
    component: { xs: 4, sm: 8, md: 12, lg: 16, xl: 24 },
  },
} as const;

Accessibility Requirements

RequirementValueNon-negotiable
Text contrast4.5:1 minimum
UI component contrast3:1 minimum
Focus indicator2px+ visible outline
Touch targets44×44pt minimum

Rule: Color is never the only indicator. Always pair with icon, text, or pattern.

Focus States

css
:focus-visible {
  outline: 2px solid var(--color-border-focus);
  outline-offset: 2px;
}

:focus:not(:focus-visible) {
  outline: none;
}

Reduced Motion

css
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

Quick Checklist

Tokens

  • No hardcoded values in components (colors, spacing, etc.)
  • Using Semantic tokens, not Primitive
  • Dark theme remaps Semantic tokens only

Components

  • Headless hook for behavior + a11y
  • Styled component uses tokens only
  • Using variant / size / colorScheme props (not booleans)
  • All states defined: default, hover, focus, active, disabled, loading

Accessibility

  • Color not sole indicator
  • Focus visible (2px+ outline)
  • Touch targets 44pt+
  • Reduced motion respected
  • ARIA attributes correct