theme-to-component-migration
系统化的从硬编码样式到主题标记和 UI 组件的迁移。识别硬编码的 Tailwind 类、内联样式和原生 HTML 元素,然后将其替换为主题标记和 Storybook 定义的 UI 组件。确保整个代码库的主题一致性和组件可重用性。
SKILL.md
--- frontmattername: 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