en un clic
extract-ui-component
// Extract reusable UI components from inline patterns. Covers component design, TypeScript props, Storybook stories, refactoring strategy, and best practices for creating shared UI primitives.
// Extract reusable UI components from inline patterns. Covers component design, TypeScript props, Storybook stories, refactoring strategy, and best practices for creating shared UI primitives.
Initialize a minimal hello-world mock-app with React, Vite, TypeScript, Tailwind CSS, React Router, and MSW. Use when scaffolding a new mock-app from scratch, bootstrapping a project skeleton, or starting a fresh prototype. Produces a working app with routing, mock API support, and a hello-world landing page.
Track and manage reusable UI components and patterns in the mock-app. Maintains an inventory of extracted components and unextracted patterns to promote incremental refactoring and prevent code duplication.
End-to-end workflow for implementing a new feature in the mock-app, ensuring data model alignment, sample data, UI/UX consistency, component reuse, and automated verification with Playwright MCP.
Create new Agent Skills for this project. Use when asked to create a skill, document a workflow, or teach Copilot a new capability. Skills are stored in .github/skills/ and can include instructions, scripts, examples, and resources.
Debug and fix failing Playwright E2E tests. Use when tests fail, when asked to fix failing tests, or when investigating test failures. Analyzes test output, screenshots, error context, and uses Playwright MCP to identify root causes.
Always start a fresh browser session after any file change, walk through the full user flow, and monitor for errors before proceeding with further work.
| name | extract-ui-component |
| description | Extract reusable UI components from inline patterns. Covers component design, TypeScript props, Storybook stories, refactoring strategy, and best practices for creating shared UI primitives. |
Guide the extraction of reusable UI components from inline code patterns. This skill ensures components are:
Goal: Understand all variations before designing the component API.
Steps:
Example for Button:
// Found patterns:
// 1. Primary: bg-blue-600 hover:bg-blue-700
// 2. Success: bg-green-600 hover:bg-green-700
// 3. Danger: bg-red-600 hover:bg-red-700
// 4. Ghost: text-blue-600 hover:text-blue-800
// Common: px-4 py-2 rounded-lg font-semibold
Goal: Create a flexible, composable API that covers all use cases.
Guidelines:
children for contentclassName override for edge casesDesign Pattern:
interface ComponentProps extends React.HTMLAttributes<HTMLElement> {
variant?: 'primary' | 'secondary' | 'success' | 'danger' | 'ghost';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
className?: string;
children: React.ReactNode;
}
API Questions to Answer:
forwardRef if needed)Location: /mock-app/src/components/ui/ComponentName.tsx
Structure:
import React from 'react';
export interface ComponentNameProps extends React.HTMLAttributes<HTMLElement> {
variant?: 'primary' | 'secondary' | 'success' | 'danger';
size?: 'sm' | 'md' | 'lg';
className?: string;
children: React.ReactNode;
}
export function ComponentName({
variant = 'primary',
size = 'md',
className = '',
children,
...props
}: ComponentNameProps) {
// Build className string based on variants
const baseClasses = 'base-classes-here';
const variantClasses = {
primary: 'variant-specific-classes',
secondary: 'variant-specific-classes',
// ...
};
const sizeClasses = {
sm: 'size-specific-classes',
md: 'size-specific-classes',
lg: 'size-specific-classes',
};
const classes = `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${className}`.trim();
return (
<element className={classes} {...props}>
{children}
</element>
);
}
Best Practices:
...propsclassName override but apply it lastLocation: /mock-app/src/components/ui/ComponentName.stories.tsx
Structure:
import type { Meta, StoryObj } from '@storybook/react';
import { ComponentName } from './ComponentName';
const meta: Meta<typeof ComponentName> = {
title: 'UI/ComponentName',
component: ComponentName,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'success', 'danger', 'ghost'],
},
size: {
control: 'select',
options: ['sm', 'md', 'lg'],
},
},
};
export default meta;
type Story = StoryObj<typeof meta>;
// Story for each variant
export const Primary: Story = {
args: {
variant: 'primary',
children: 'Primary Button',
},
};
export const Secondary: Story = {
args: {
variant: 'secondary',
children: 'Secondary Button',
},
};
// Showcase all variants
export const AllVariants: Story = {
render: () => (
<div className="flex gap-4">
<ComponentName variant="primary">Primary</ComponentName>
<ComponentName variant="secondary">Secondary</ComponentName>
<ComponentName variant="success">Success</ComponentName>
<ComponentName variant="danger">Danger</ComponentName>
</div>
),
};
// Showcase all sizes
export const AllSizes: Story = {
render: () => (
<div className="flex gap-4 items-center">
<ComponentName size="sm">Small</ComponentName>
<ComponentName size="md">Medium</ComponentName>
<ComponentName size="lg">Large</ComponentName>
</div>
),
};
// Showcase states (if applicable)
export const Disabled: Story = {
args: {
disabled: true,
children: 'Disabled Button',
},
};
Story Guidelines:
render function for complex examplestags: ['autodocs'] for auto-generated docsSteps:
npm run storybookChecklist:
Location: /mock-app/src/components/ui/index.ts
export { ComponentName } from './ComponentName';
export type { ComponentNameProps } from './ComponentName';
This allows clean imports: import { Button } from '@/components/ui';
Strategy: Refactor incrementally, one file at a time.
Steps:
import { ComponentName } from '@/components/ui/ComponentName';Example Refactoring:
// Before
<button className="bg-blue-600 hover:bg-blue-700 text-white font-semibold px-4 py-2 rounded-lg shadow">
Submit
</button>
// After
<Button variant="primary" onClick={handleSubmit}>
Submit
</Button>
Refactoring Tips:
Add to "✅ Extracted Components":
### ComponentName
- **Location**: `/mock-app/src/components/ui/ComponentName.tsx`
- **Storybook**: `ComponentName.stories.tsx`
- **Props**: variant (primary | secondary | ...), size (sm | md | lg), children, className, ...HTMLAttributes
- **Usage Count**: X usages across Y files
- **Status**: ✅ EXTRACTED
Update Maintenance Log:
| 2026-02-24 | Extracted ComponentName | Refactored X instances across Y files |
Remove from "⚠️ Patterns Needing Extraction":
<button> element (not <div> with click handler)type attribute (button, submit, reset)aria-label if button has no text (icon-only)aria-disabled when disabled<article>, <section>)<span> elementclassName for overrides...props for HTML attributesany or unclear typesSee the implementation workflow:
/mock-app/src/components/ui/Button.tsxButton.stories.tsx with 8+ storiescomponent-registry: For tracking components and patternsimplement-feature: When to extract components during feature work