
ui-component-builder
by deve1993
SKILL.md
name: ui-component-builder description: Build, convert, and optimize React/Vue/Svelte UI components with TypeScript, advanced patterns, animations, and accessibility allowed-tools:
- Read
- Write
- Edit
- Bash
- Glob
- Grep
- Task
UI Component Builder
Expert skill for building production-ready UI components for component libraries. Specializes in React, Vue, and Svelte with TypeScript, implementing advanced patterns, animations, and ensuring accessibility and performance.
Core Capabilities
1. Component Creation
- Create new components from scratch with TypeScript
- Implement compound components pattern
- Build components with render props
- Create custom hooks and composables
- Support for polymorphic components (as prop pattern)
- Generic component types for maximum reusability
2. Framework Conversion
Convert components seamlessly between frameworks:
- React → Vue: JSX to template/composition API
- React → Svelte: Hooks to reactive declarations
- Vue → React: Template to JSX, composables to hooks
- Svelte → React: Reactive syntax to useState/useEffect
Preserve functionality, props interface, and behavior while adapting to framework idioms.
3. Advanced Patterns Implementation
Compound Components
// Parent component manages shared state
// Child components access via context
<Select>
<Select.Trigger />
<Select.Content>
<Select.Item value="1">Option 1</Select.Item>
</Select.Content>
</Select>
Render Props
<DataFetcher url="/api/data">
{({ data, loading, error }) => (
// Render logic here
)}
</DataFetcher>
Custom Hooks Pattern
// Extract reusable logic
const { isOpen, open, close, toggle } = useDisclosure()
4. Animation Integration
- Integrate Framer Motion for React components
- Use transition/animation APIs for Vue/Svelte
- Create spring physics animations
- Implement gesture controls (drag, hover, tap)
- Build enter/exit animations
- Orchestrate complex animation sequences
- Optimize for 60fps performance
5. TypeScript Excellence
- Strict type safety with proper generic constraints
- Discriminated unions for component variants
- Type inference for props and state
- Utility types (Omit, Pick, Required, Partial)
- Type guards and narrowing
- Proper typing for refs, events, and children
6. Accessibility (a11y) First
- Semantic HTML elements
- ARIA attributes when needed (roles, labels, states)
- Keyboard navigation (Tab, Enter, Escape, Arrow keys)
- Focus management and focus trapping
- Screen reader announcements
- Color contrast compliance (WCAG AA/AAA)
- Reduced motion support
7. Performance Optimization
- React.memo / Vue computed / Svelte reactive statements
- useMemo, useCallback optimization
- Virtual scrolling for large lists
- Code splitting and lazy loading
- Bundle size analysis
- Avoid unnecessary re-renders
- Optimize event handlers
Workflow
Phase 1: Analysis & Planning
-
Understand Requirements
- Component purpose and use cases
- Required props and variants
- Accessibility requirements
- Animation needs
- Performance constraints
-
Review Existing Code
- Check for similar components
- Identify reusable patterns
- Review design system tokens
- Check naming conventions
-
Plan Implementation
- Choose appropriate patterns
- Define component API (props interface)
- Plan state management approach
- Identify edge cases
Phase 2: Implementation
-
Create Base Structure
- Set up component file(s)
- Define TypeScript interfaces
- Implement basic rendering
- Add prop validation
-
Implement Core Logic
- Add state management
- Implement event handlers
- Apply business logic
- Handle edge cases
-
Add Styling
- Use design system tokens
- Implement variants (size, color, state)
- Add responsive behavior
- Support theming (light/dark)
-
Enhance with Features
- Add animations if needed
- Implement keyboard navigation
- Add ARIA attributes
- Create controlled/uncontrolled variants
Phase 3: Quality Assurance
-
Accessibility Check
- Test keyboard navigation
- Verify ARIA attributes
- Check screen reader output
- Validate color contrast
-
Performance Review
- Check for unnecessary renders
- Optimize heavy computations
- Review bundle impact
- Test with React DevTools Profiler
-
Type Safety Verification
- No TypeScript errors
- Props are properly typed
- Type inference works correctly
- Generic constraints are sound
-
Cross-browser Testing
- Modern browsers support
- Graceful degradation
- Polyfills if needed
Component Patterns Library
Button Component Template
import { forwardRef, type ButtonHTMLAttributes } from 'react'
import { cva, type VariantProps } from 'class-variance-authority'
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
outline: 'border border-input bg-background hover:bg-accent',
ghost: 'hover:bg-accent hover:text-accent-foreground',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
)
interface ButtonProps
extends ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, ...props }, ref) => {
return (
<button
ref={ref}
className={buttonVariants({ variant, size, className })}
{...props}
/>
)
}
)
Button.displayName = 'Button'
export { Button, buttonVariants }
Compound Component Template
import { createContext, useContext, useState } from 'react'
interface TabsContextValue {
activeTab: string
setActiveTab: (tab: string) => void
}
const TabsContext = createContext<TabsContextValue | undefined>(undefined)
export function Tabs({ defaultValue, children }: TabsProps) {
const [activeTab, setActiveTab] = useState(defaultValue)
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
<div>{children}</div>
</TabsContext.Provider>
)
}
export function TabsList({ children }: TabsListProps) {
return <div role="tablist">{children}</div>
}
export function TabsTrigger({ value, children }: TabsTriggerProps) {
const context = useContext(TabsContext)
const isActive = context?.activeTab === value
return (
<button
role="tab"
aria-selected={isActive}
onClick={() => context?.setActiveTab(value)}
>
{children}
</button>
)
}
export function TabsContent({ value, children }: TabsContentProps) {
const context = useContext(TabsContext)
if (context?.activeTab !== value) return null
return <div role="tabpanel">{children}</div>
}
Animated Component Template (Framer Motion)
import { motion, AnimatePresence } from 'framer-motion'
import { useState } from 'react'
export function Modal({ isOpen, onClose, children }: ModalProps) {
return (
<AnimatePresence>
{isOpen && (
<>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={onClose}
className="fixed inset-0 bg-black/50"
/>
<motion.div
initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95, y: 20 }}
transition={{ type: 'spring', duration: 0.3 }}
className="fixed inset-0 flex items-center justify-center"
>
<div className="bg-white rounded-lg p-6 max-w-md w-full">
{children}
</div>
</motion.div>
</>
)}
</AnimatePresence>
)
}
Framework Conversion Guidelines
React to Vue Conversion
// REACT
function Counter() {
const [count, setCount] = useState(0)
useEffect(() => {
console.log('Count changed:', count)
}, [count])
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
// VUE (Composition API)
<script setup lang="ts">
import { ref, watch } from 'vue'
const count = ref(0)
watch(count, (newCount) => {
console.log('Count changed:', newCount)
})
</script>
<template>
<button @click="count++">{{ count }}</button>
</template>
React to Svelte Conversion
// REACT
function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
// SVELTE
<script lang="ts">
let count = 0
$: console.log('Count changed:', count)
</script>
<button on:click={() => count++}>
{count}
</button>
Best Practices
Component Structure
- One component per file (with related subcomponents)
- Co-locate types, styles, and utilities
- Export component as named export
- Provide displayName for debugging
Props Design
- Keep props interface simple and focused
- Use discriminated unions for variants
- Provide sensible defaults
- Support both controlled and uncontrolled modes
- Accept className for style overrides
- Forward refs when wrapping native elements
State Management
- Lift state up when needed by multiple components
- Keep component-specific state local
- Use context sparingly (avoid prop drilling)
- Prefer composition over context
Naming Conventions
- PascalCase for components
- camelCase for props and functions
- SCREAMING_SNAKE_CASE for constants
- Prefix custom hooks with "use"
- Prefix event handlers with "handle" or "on"
File Organization
components/
Button/
Button.tsx # Main component
Button.types.ts # Type definitions
Button.styles.ts # Styling (if separate)
Button.test.tsx # Unit tests
index.ts # Public exports
Accessibility Checklist
- Semantic HTML elements used
- Keyboard navigation works
- Focus visible and manageable
- ARIA attributes where needed
- Screen reader tested
- Color contrast meets WCAG AA
- Supports prefers-reduced-motion
Performance Checklist
- No unnecessary re-renders
- Heavy computations memoized
- Event handlers optimized
- Bundle size impact minimal
- Lazy loading where appropriate
- Virtual scrolling for large lists
Common Patterns
Polymorphic Component (as prop)
type AsProp<C extends React.ElementType> = {
as?: C
}
type PropsToOmit<C extends React.ElementType, P> = keyof (AsProp<C> & P)
type PolymorphicComponentProp<
C extends React.ElementType,
Props = {}
> = React.PropsWithChildren<Props & AsProp<C>> &
Omit<React.ComponentPropsWithoutRef<C>, PropsToOmit<C, Props>>
type TextProps<C extends React.ElementType> = PolymorphicComponentProp<
C,
{ color?: 'primary' | 'secondary' }
>
export function Text<C extends React.ElementType = 'span'>({
as,
color = 'primary',
children,
...props
}: TextProps<C>) {
const Component = as || 'span'
return <Component className={color} {...props}>{children}</Component>
}
// Usage: <Text as="h1">Title</Text>
Controlled/Uncontrolled Pattern
interface InputProps {
value?: string // Controlled
defaultValue?: string // Uncontrolled
onChange?: (value: string) => void
}
function Input({ value, defaultValue, onChange }: InputProps) {
const [internalValue, setInternalValue] = useState(defaultValue ?? '')
const isControlled = value !== undefined
const currentValue = isControlled ? value : internalValue
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value
if (!isControlled) {
setInternalValue(newValue)
}
onChange?.(newValue)
}
return <input value={currentValue} onChange={handleChange} />
}
Focus Trap (for Modals)
import { useEffect, useRef } from 'react'
function useFocusTrap(isActive: boolean) {
const containerRef = useRef<HTMLDivElement>(null)
useEffect(() => {
if (!isActive) return
const container = containerRef.current
if (!container) return
const focusableElements = container.querySelectorAll(
'a[href], button, textarea, input, select, [tabindex]:not([tabindex="-1"])'
)
const firstElement = focusableElements[0] as HTMLElement
const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement
const handleTab = (e: KeyboardEvent) => {
if (e.key !== 'Tab') return
if (e.shiftKey && document.activeElement === firstElement) {
e.preventDefault()
lastElement.focus()
} else if (!e.shiftKey && document.activeElement === lastElement) {
e.preventDefault()
firstElement.focus()
}
}
container.addEventListener('keydown', handleTab)
firstElement?.focus()
return () => container.removeEventListener('keydown', handleTab)
}, [isActive])
return containerRef
}
Tools & Dependencies
Essential Tools
- TypeScript: Type safety and better DX
- class-variance-authority (cva): Variant management
- clsx / cn: Conditional className composition
- Framer Motion: Advanced animations
- React Hook Form: Form state management
- Zod: Runtime validation
Development Tools
- Vite: Fast development server
- tsup: TypeScript bundler
- Vitest: Testing framework
- React Testing Library: Component testing
- Storybook: Component development
Tips for Success
- Start Simple: Build basic functionality first, then add complexity
- Think in Components: Break down complex UIs into smaller pieces
- Composition Over Inheritance: Use composition patterns
- Test Early: Write tests as you build
- Document Props: Add JSDoc comments for prop descriptions
- Use Design Tokens: Reference design system values
- Consider Edge Cases: Empty states, loading, errors
- Mobile First: Design for mobile, enhance for desktop
- Performance Budget: Keep bundle size in check
- Accessibility Always: Build accessible from the start
When to Use This Skill
Activate this skill when you need to:
- Create new UI components from scratch
- Convert components between React/Vue/Svelte
- Implement advanced component patterns
- Add animations to components
- Optimize component performance
- Improve component accessibility
- Refactor existing components
- Build compound components
- Create polymorphic components
- Implement custom hooks/composables
Output Format
When creating components, provide:
- Complete Component Code: Fully typed and production-ready
- Usage Example: Show how to use the component
- Props Documentation: Describe all props with types
- Accessibility Notes: Keyboard shortcuts and ARIA usage
- Performance Tips: Any optimization considerations
- Styling Notes: How to customize appearance
Always follow the project's existing conventions and patterns.
Score
Total Score
Based on repository quality metrics
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 100以上
3ヶ月以内に更新がある
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
Reviews
Reviews coming soon