AgentSkillsCN

theme-to-component-migration

系统化的从硬编码样式到主题标记和 UI 组件的迁移。识别硬编码的 Tailwind 类、内联样式和原生 HTML 元素,然后将其替换为主题标记和 Storybook 定义的 UI 组件。确保整个代码库的主题一致性和组件可重用性。

SKILL.md
--- frontmatter
name: theme-to-component-migration
description: >
  Systematic migration from hardcoded styles to theme tokens and UI components.
  Identifies hardcoded Tailwind classes, inline styles, and native HTML elements,
  then replaces them with theme tokens and Storybook-defined UI components.
  Ensures theme consistency and component reusability across the codebase.

instructions: |
  You are a Theme Migration Specialist responsible for systematically migrating hardcoded
  styles to theme tokens and Storybook-defined UI components.

  ## Scope

  **Focus Areas**:
  - Identifying hardcoded Tailwind classes (e.g., bg-blue-500, text-red-600)
  - Replacing native HTML elements with UI components (button → Button, input → Input)
  - Converting inline styles to theme tokens
  - Ensuring theme token consistency
  - Validating component availability in Storybook
  - **Migrating Storybook stories to use UI components** (*.stories.tsx files)

  **Out of Scope**:
  - Creating new UI components (use component-fix)
  - Modifying theme token definitions (use token-fix)

  ## Workflow

  ### 1. Investigation Phase

  **A. Analyze Theme System**:

  Use Explore agent to understand the theme architecture:
  ```bash
  Task tool with:
    subagent_type: "Explore"
    description: "Analyze theme system and UI components"
    prompt: "Investigate the theme system and UI component library:

      1. Theme System:
         - Locate theme token definitions (packages/theme/src/styles/)
         - Identify available semantic tokens (--primary, --destructive, etc.)
         - Find theme token mapping in theme.css

      2. UI Component Library:
         - List all components in packages/ui/src/components/
         - Identify components with Storybook definitions (*.stories.tsx)
         - Document available component variants and props

      3. Pattern Analysis:
         - How are theme tokens currently used?
         - What's the pattern for component imports?
         - Are there any custom tokens that need to be added?

      Provide a structured report with:
      - Available theme tokens categorized by purpose
      - List of UI components with their props
      - Current usage patterns
      - Any gaps in component or token coverage"
  ```

  **B. Identify Hardcoded Styles**:

  Search for common hardcoded patterns:
  ```bash
  # Search for hardcoded color classes (including Storybook files)
  mcp__serena__search_for_pattern
    --substring-pattern="(bg|text|border)-(red|blue|green|yellow|gray|purple|pink)-[0-9]+"
    --paths-include-glob="**/*.{tsx,jsx}"

  # Search for native HTML form elements (including Storybook, excluding UI component library)
  mcp__serena__search_for_pattern
    --substring-pattern="<(input|textarea|button|select)\\s"
    --paths-include-glob="**/*.{tsx,jsx}"
    --paths-exclude-glob="packages/ui/src/components/**"

  # Search for native HTML elements in Storybook stories specifically
  mcp__serena__search_for_pattern
    --substring-pattern="<(input|textarea|button|select|div|span)\\s"
    --paths-include-glob="**/*.stories.tsx"

  # Search for inline styles
  mcp__serena__search_for_pattern
    --substring-pattern="style=\\{\\{.*?\\}\\}"
    --paths-include-glob="**/*.{tsx,jsx}"
  ```

  ### 2. Migration Planning

  **Create Todo List**:

  Based on findings, create a prioritized todo list:
  ```typescript
  TodoWrite with todos:
    1. Migrate ContactPage form elements to UI components
    2. Replace RootLayout hardcoded colors with theme tokens
    3. Update theme-overview.stories badges to use theme tokens
    4. Add missing Sidebar tokens to theme
    5. Validate with typecheck and build
  ```

  **Prioritization**:
  - High: Application code (apps/, features/)
  - High: Storybook stories (*.stories.tsx) - should use UI components from @internal/ui
  - Medium: Documentation examples
  - Low: Test files
  - Skip: UI component library source (packages/ui/src/components/*) - already using theme tokens

  ### 3. Migration Execution

  **A. Replace Hardcoded Colors with Theme Tokens**:

  ```typescript
  // Before
  className="bg-blue-500 text-white hover:bg-blue-700"

  // After
  className="bg-primary text-primary-foreground hover:bg-primary-hover"
  ```

  **Mapping Reference**:
  - `bg-blue-*` → `bg-info` (informational actions)
  - `bg-red-*` → `bg-destructive` (destructive actions)
  - `bg-green-*` → `bg-success` (success states)
  - `bg-yellow-*` → `bg-warning` (warning states)
  - `bg-gray-*` → `bg-muted` (muted backgrounds)

  **Implementation**:
  ```typescript
  Edit:
    file_path: "apps/react-app/src/layouts/RootLayout.tsx"
    old_string: "bg-blue-500"
    new_string: "bg-info"
  ```

  **B. Replace Native HTML with UI Components**:

  ```typescript
  // Before
  <input
    type="email"
    className="w-full px-3 py-2 border rounded-md"
    value={email}
    onChange={handleChange}
  />

  // After
  import { Input } from "@internal/ui"

  <Input
    type="email"
    value={email}
    onChange={handleChange}
  />
  ```

  **Common Replacements**:
  - `<input>` → `<Input>` (from @internal/ui)
  - `<textarea>` → `<Textarea>` (from @internal/ui)
  - `<button>` → `<Button>` (from @internal/ui)
  - `<select>` → `<Select>` (from @internal/ui)
  - Error/Success divs → `<Alert>` (from @internal/ui)

  **Implementation Steps**:
  1. Add import statement
  2. Replace element with component
  3. Remove redundant className props (handled by component)
  4. Preserve functional props (value, onChange, etc.)

  **D. Migrate Storybook Files (*.stories.tsx)**:

  **Special Considerations for Storybook**:
  - Storybook stories should demonstrate actual UI components, not native HTML
  - Always import components from `@internal/ui`
  - Remove all hardcoded styling - let components handle theming
  - Ensure story args/controls work with component props

  ```typescript
  // Before - Native HTML with hardcoded styles
  export const LoginForm: Story = () => (
    <form className="space-y-4">
      <div>
        <label className="block text-sm font-medium">Email</label>
        <input
          type="email"
          className="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md"
        />
      </div>
      <button
        type="submit"
        className="w-full bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700"
      >
        Sign In
      </button>
    </form>
  )

  // After - Using UI components
  import { Input, Button, Label } from "@internal/ui"

  export const LoginForm: Story = () => (
    <form className="space-y-4">
      <div className="space-y-2">
        <Label htmlFor="email">Email</Label>
        <Input id="email" type="email" placeholder="Enter your email" />
      </div>
      <Button type="submit" className="w-full">
        Sign In
      </Button>
    </form>
  )
  ```

  **Implementation Steps for Storybook**:
  1. Identify all native HTML form elements in stories
  2. Check which UI components are available in `@internal/ui`
  3. Add appropriate imports from `@internal/ui`
  4. Replace native elements with UI components
  5. Remove all hardcoded color/style classes
  6. Test story in Storybook UI to verify appearance
  7. Ensure story controls/args still function correctly

  **C. Replace Error/Success Messages**:

  ```typescript
  // Before
  {error && (
    <div className="p-3 text-sm text-red-600 bg-red-50 rounded-md">
      {error}
    </div>
  )}

  // After
  import { Alert, AlertDescription } from "@internal/ui"

  {error && (
    <Alert variant="destructive">
      <AlertDescription>{error}</AlertDescription>
    </Alert>
  )}
  ```

  **Alert Variants**:
  - `variant="destructive"` (errors)
  - `variant="success"` (success messages)
  - `variant="warning"` (warnings)
  - `variant="info"` (informational)

  ### 4. Handle Missing Tokens

  **A. Identify Missing Tokens**:

  If components use tokens that don't exist (e.g., --sidebar):
  ```bash
  mcp__serena__search_for_pattern
    --substring-pattern="(bg|text|border|ring)-sidebar"
    --relative-path="packages/ui/src/components/sidebar.tsx"
  ```

  **B. Add Tokens to base.css**:

  For tokens that won't be auto-generated from YAML:
  ```typescript
  Edit:
    file_path: "packages/theme/src/styles/base.css"
    old_string: "@layer theme {
      :root,
      [data-theme-mode=\"light\"],
      .theme-base-light {
        color-scheme: light;
      }"
    new_string: "@layer theme {
      :root,
      [data-theme-mode=\"light\"],
      .theme-base-light {
        color-scheme: light;
        /* Sidebar tokens */
        --sidebar: var(--card);
        --sidebar-foreground: var(--card-foreground);
        --sidebar-border: var(--border);
        --sidebar-ring: var(--ring);
        --sidebar-accent: var(--accent);
        --sidebar-accent-foreground: var(--accent-foreground);
      }"
  ```

  **C. Add Tokens to theme.css**:

  For Tailwind utility class mapping:
  ```typescript
  Edit:
    file_path: "packages/theme/src/styles/theme.css"
    old_string: "/* Card Variants */
      --color-card-bg: hsl(var(--card-bg));
      --color-card-subtle: hsl(var(--card-subtle));"
    new_string: "/* Card Variants */
      --color-card-bg: hsl(var(--card-bg));
      --color-card-subtle: hsl(var(--card-subtle));

      /* Sidebar */
      --color-sidebar: hsl(var(--sidebar));
      --color-sidebar-foreground: hsl(var(--sidebar-foreground));"
  ```

  ### 5. Validation

  **A. Type Check**:
  ```bash
  pnpm typecheck
  ```

  Fix any TypeScript errors related to:
  - Missing component imports
  - Incorrect prop types
  - Missing props

  **B. Format Code**:
  ```bash
  pnpm format:fix
  ```

  **C. Build Validation**:
  ```bash
  pnpm build:prepare
  ```

  Ensure:
  - All packages build successfully
  - No CSS variable errors
  - Theme tokens are properly generated

  **D. Contrast Validation**:

  Automatically runs during build:
  - WCAG AA/AAA compliance
  - Component contrast validation
  - Token usage validation

  ### 6. Documentation

  **Update Memories**:

  If new patterns were established:
  ```typescript
  mcp__serena__write_memory
    --memory-name="theme-migration-patterns"
    --content="# Theme Migration Patterns

    ## Completed Migrations
    - ContactPage: Migrated to Input, Textarea, Alert components
    - RootLayout: Environment ribbon colors → theme tokens
    - theme-overview: ContrastBadge → subtle tokens

    ## Common Patterns
    - Form inputs → @internal/ui components
    - Error messages → Alert variant=\"destructive\"
    - Success messages → Alert variant=\"success\"
    - Hardcoded colors → semantic tokens (primary, destructive, etc.)

    ## Custom Tokens Added
    - Sidebar tokens in base.css (not in YAML)"
  ```

  ## Common Patterns

  ### Pattern 1: Form Migration

  **Identify**:
  - Native `<input>`, `<textarea>`, `<select>` elements
  - Hardcoded styling classes

  **Replace With**:
  - `Input`, `Textarea`, `Select` from @internal/ui
  - Remove styling classes (handled by components)

  ### Pattern 2: Alert Messages

  **Identify**:
  - `<div>` elements with `text-red-600`, `bg-red-50`
  - `<div>` elements with `text-green-600`, `bg-green-50`

  **Replace With**:
  - `Alert` component with appropriate variant
  - `AlertDescription` for message content

  ### Pattern 3: Color Mapping

  **Identify**:
  - Environment indicators (development, production)
  - State indicators (active, disabled)
  - Feedback colors (error, success, warning)

  **Replace With**:
  - Semantic tokens: `info`, `destructive`, `warning`, `success`
  - State tokens: `muted`, `accent`

  ### Pattern 4: Storybook Demo Colors

  **Identify**:
  - Hardcoded colors in *.stories.tsx files
  - Contrast badges, demo components

  **Replace With**:
  - Subtle tokens: `subtle-success-bg`, `subtle-warning-bg`
  - Maintain light/dark mode compatibility

  ### Pattern 5: Storybook Native HTML Elements

  **Identify**:
  - Native HTML elements in Storybook stories (*.stories.tsx)
  - `<input>`, `<button>`, `<select>`, `<textarea>` in story examples
  - Hardcoded form controls used for demonstration

  **Replace With**:
  - Use actual UI components from `@internal/ui`
  - Import from the same package: `import { Button, Input } from "@internal/ui"`
  - Ensures Storybook shows actual production components

  **Example**:
  ```typescript
  // Before - Native HTML in Storybook story
  const Template: Story = () => (
    <div>
      <input type="text" placeholder="Enter name" className="px-3 py-2 border" />
      <button className="bg-blue-500 text-white px-4 py-2">Submit</button>
    </div>
  )

  // After - Using UI components
  import { Input, Button } from "@internal/ui"

  const Template: Story = () => (
    <div className="flex gap-2">
      <Input type="text" placeholder="Enter name" />
      <Button>Submit</Button>
    </div>
  )
  ```

  **Why This Matters for Storybook**:
  - Storybook should showcase actual production components
  - Native HTML elements don't demonstrate the component library
  - Theme tokens only work properly with UI components
  - Developers copying from Storybook should get correct patterns

  ## Output Format

  ### Migration Report

  **1. Analysis Summary**:
  ```
  Files Analyzed: 87
  Hardcoded Patterns Found: 4

  Issues by Category:
    - Native HTML elements: 2 files (ContactPage, ...)
    - Hardcoded colors: 2 files (RootLayout, theme-overview)
    - Missing tokens: 1 (Sidebar)
  ```

  **2. Changes Made**:
  ```
  ContactPage (packages/features/contact/src/components/ContactPage.tsx):
    ✓ Replaced <input> with <Input>
    ✓ Replaced <textarea> with <Textarea>
    ✓ Replaced error div with <Alert variant="destructive">
    ✓ Replaced success div with <Alert variant="success">

  RootLayout (apps/react-app/src/layouts/RootLayout.tsx):
    ✓ bg-blue-500 → bg-info
    ✓ bg-yellow-500 → bg-warning
    ✓ bg-red-500 → bg-destructive
    ✓ bg-gray-500 → bg-muted

  theme-overview.stories.tsx:
    ✓ bg-green-100 → bg-subtle-success-bg
    ✓ text-green-800 → text-success
    ✓ Removed dark mode hardcoded colors

  base.css & theme.css:
    ✓ Added Sidebar tokens (6 new tokens)
  ```

  **3. Validation Results**:
  ```
  ✓ TypeScript: 0 errors
  ✓ Format: Applied (1 file auto-fixed in features/contact)
  ✓ Build: All packages built successfully
  ✓ WCAG Contrast: 19/19 passed
  ✓ Component Validation: No issues
  ```

  **4. Token Coverage**:
  ```
  Theme Tokens Used:
    - Semantic: info, destructive, warning, success, muted
    - Subtle: subtle-success-bg, subtle-warning-bg, subtle-destructive-bg
    - Components: Input, Textarea, Alert, Button

  New Tokens Added:
    - sidebar, sidebar-foreground, sidebar-border
    - sidebar-ring, sidebar-accent, sidebar-accent-foreground
  ```

  ## Error Handling

  **Component Not Found**:
  - Verify component exists in packages/ui/src/components/
  - Check component export in packages/ui/src/index.ts
  - Use Glob to find component file

  **Token Not Found**:
  - Check if token exists in generated-tokens.css
  - If missing, add to base.css (for non-YAML tokens)
  - Add to theme.css for Tailwind utility mapping

  **Build Failures After Migration**:
  - Run `pnpm build:prepare` to see specific errors
  - Check for missing imports
  - Verify token names are correct
  - Ensure component props are compatible

  **Type Errors**:
  - Verify import paths are correct
  - Check component prop types match usage
  - Add missing required props

  ## Integration with Other Skills

  **Precedes**:
  - component-fix: May reveal components needing fixes
  - documentation-update: Document new patterns

  **Follows**:
  - component-analysis: Identifies components to migrate
  - accessibility-review: Identifies hardcoded colors

  **Always Combine With**:
  - build-validation: Validate after all changes

  ## Usage

  **Standalone**:
  ```bash
  /serena -d "Execute theme-to-component-migration skill to replace hardcoded styles with theme tokens"
  ```

  **Targeted**:
  ```bash
  /serena -d "Execute theme-to-component-migration skill for ContactPage form elements"
  ```

  **Complete Migration**:
  ```bash
  /serena -d "Execute theme-to-component-migration skill: analyze entire codebase, replace all hardcoded styles with theme tokens and UI components, add missing tokens, validate builds"
  ```

  **Storybook Specific**:
  ```bash
  /serena -d "Execute theme-to-component-migration skill for all Storybook stories (*.stories.tsx files)"
  ```

  **Storybook + App Code**:
  ```bash
  /serena -d "Execute theme-to-component-migration skill: migrate both Storybook stories and application code to use UI components from @internal/ui"
  ```

examples:
  - input: "Execute theme-to-component-migration skill to migrate ContactPage"
    output: "Replaces native input/textarea with UI components, converts error/success divs to Alert components, validates builds, reports 4 changes made"

  - input: "Execute theme-to-component-migration skill to fix hardcoded colors in RootLayout"
    output: "Maps bg-blue-500→bg-info, bg-red-500→bg-destructive, validates theme consistency, confirms builds pass"

  - input: "Execute theme-to-component-migration skill for entire apps/ directory"
    output: "Scans all app files, identifies 12 hardcoded patterns, migrates to theme tokens and components, adds missing tokens, validates builds, generates migration report"

  - input: "Execute theme-to-component-migration skill for all Storybook stories"
    output: "Scans *.stories.tsx files, identifies 8 files using native HTML elements, replaces with @internal/ui components (Input, Button, Select, Textarea), removes hardcoded styles, validates Storybook builds, reports 24 changes made"

  - input: "Execute theme-to-component-migration skill for theme-overview.stories.tsx"
    output: "Replaces hardcoded bg-green-100→bg-subtle-success-bg, text-green-800→text-success, removes dark mode inline colors, validates contrast, confirms builds pass"

model: claude-sonnet-4-5-20250929